<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222"><meta name="generator" content="Hexo 6.3.0">

  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css" integrity="sha256-Z1K5uhUaJXA7Ll0XrZ/0JhX4lAtZFpT6jkKrEDT0drU=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">

<script class="next-config" data-name="main" type="application/json">{"hostname":"example.com","root":"/","images":"/images","scheme":"Muse","darkmode":false,"version":"8.14.1","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":{"enable":false,"style":null},"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":false,"transition":{"menu_item":"fadeInDown","post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"},"path":"/search.xml","localsearch":{"enable":true,"trigger":"auto","top_n_per_article":-1,"unescape":false,"preload":false}}</script><script src="/js/config.js"></script>

    <meta property="og:type" content="website">
<meta property="og:title" content="JsyBlog">
<meta property="og:url" content="http://example.com/page/18/index.html">
<meta property="og:site_name" content="JsyBlog">
<meta property="og:locale" content="zh_CN">
<meta property="article:author" content="SongyangJi">
<meta name="twitter:card" content="summary">


<link rel="canonical" href="http://example.com/page/18/">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":true,"isPost":false,"lang":"zh-CN","comments":"","permalink":"","path":"page/18/index.html","title":""}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>JsyBlog</title>
  








  <noscript>
    <link rel="stylesheet" href="/css/noscript.css">
  </noscript>
</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <div class="column">
      <header class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <h1 class="site-title">JsyBlog</h1>
      <i class="logo-line"></i>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger" aria-label="搜索" role="button">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a></li><li class="menu-item menu-item-tags"><a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a></li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup"><div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off" maxlength="80"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close" role="button">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div class="search-result-container no-result">
  <div class="search-result-icon">
    <i class="fa fa-spinner fa-pulse fa-5x"></i>
  </div>
</div>

    </div>
  </div>

</header>
        
  
  <aside class="sidebar">

    <div class="sidebar-inner sidebar-overview-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">SongyangJi</p>
  <div class="site-description" itemprop="description"></div>
</div>
<div class="site-state-wrap animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/archives/">
          <span class="site-state-item-count">251</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/categories/">
        <span class="site-state-item-count">45</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
          <a href="/tags/">
        <span class="site-state-item-count">109</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>

        </div>
      </div>
    </div>

    
  </aside>


    </div>

    <div class="main-inner index posts-expand">

    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/11/29/Jmeter%E4%BD%BF%E7%94%A8/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/11/29/Jmeter%E4%BD%BF%E7%94%A8/" class="post-title-link" itemprop="url">Jmeter使用</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-11-29 10:18:42" itemprop="dateCreated datePublished" datetime="2021-11-29T10:18:42+08:00">2021-11-29</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2021-12-17 23:03:46" itemprop="dateModified" datetime="2021-12-17T23:03:46+08:00">2021-12-17</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E6%B5%8B%E8%AF%95/" itemprop="url" rel="index"><span itemprop="name">测试</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h2 id="Add-Threads（Users）添加线程组"><a href="#Add-Threads（Users）添加线程组" class="headerlink" title="Add Threads（Users）添加线程组"></a>Add Threads（Users）添加线程组</h2><p><strong>线程组主要参数详解</strong></p>
<ol>
<li>线程数：虚拟用户数。一个虚拟用户占用一个进程或线程。模拟多少用户访问也就填写多少个线程数量。</li>
<li>Ramp-Up时间(秒)：设置的虚拟用户数需要多长时间全部启动。如果线程数为<code>100</code>，准备时长为<code>5</code>，那么需要<code>5</code>秒钟启动<code>100</code>个线程，也就是每秒钟启动<code>20</code>个线程。 相当于每秒模拟<code>20</code>个用户进行访问，设置为零我理解为并发访问。</li>
<li>循环次数：如果线程数为<code>100</code>，循环次数为<code>100</code>。那么总请求数为<code>100*100=10000</code> 。如果勾选了“永远”，那么所有线程会一直发送请求，直到选择停止运行脚本。</li>
</ol>
<h2 id="Add-Sample-添加取样器（）"><a href="#Add-Sample-添加取样器（）" class="headerlink" title="Add Sample 添加取样器（）"></a>Add Sample 添加取样器（）</h2><h3 id="Add-Sample-HTTP-Request-添加http接口测试（）"><a href="#Add-Sample-HTTP-Request-添加http接口测试（）" class="headerlink" title="Add Sample HTTP Request 添加http接口测试（）"></a>Add Sample HTTP Request 添加http接口测试（）</h3><p><strong>Http请求主要参数详解</strong></p>
<ul>
<li>协议：向目标服务器发送HTTP请求协议，可以是<code>HTTP</code>或<code>HTTPS</code>，默认为<code>HTTP</code>。</li>
<li>服务器名称或IP ：<code>HTTP</code>请求发送的目标服务器名称或<code>IP</code>。</li>
<li>端口号：目标服务器的端口号，默认值为80</li>
<li>方法：发送<code>HTTP</code>请求的方法，可用方法包括<code>GET</code>、<code>POST</code>、<code>HEAD</code>、<code>PUT</code>、<code>OPTIONS</code>、<code>TRACE</code>、<code>DELETE</code>等。</li>
<li>路径：目标<code>URL</code>路径（<code>URL</code>中去掉服务器地址、端口及参数后剩余部分）。</li>
<li>字符集编码：编码方式，默认为<code>ISO-8859-1</code>编码，这里配置为<code>utf-8</code>。</li>
<li>参数：同请求一起发送参数 ，在请求中发送的<code>URL</code>参数，用户可以将<code>URL</code>中所有参数设置在本表中，表中每行为一个参数（对应<code>URL</code>中的 <code>key=value</code>），注意参数传入中文时需要勾选“编码”。</li>
</ul>
<h2 id="断言"><a href="#断言" class="headerlink" title="断言"></a>断言</h2><h3 id="Add-Assertions-Json-Assertion-添加Json断言（）"><a href="#Add-Assertions-Json-Assertion-添加Json断言（）" class="headerlink" title="Add Assertions Json Assertion 添加Json断言（）"></a>Add Assertions Json Assertion 添加Json断言（）</h3><h3 id="Add-Listener-Assertion-Results-添加断言结果（）"><a href="#Add-Listener-Assertion-Results-添加断言结果（）" class="headerlink" title="Add Listener Assertion Results 添加断言结果（）"></a>Add Listener Assertion Results 添加断言结果（）</h3><h2 id="结果报告"><a href="#结果报告" class="headerlink" title="结果报告"></a>结果报告</h2><h3 id="Add-Listener-View-Results-Tree-添加察看结果树（）"><a href="#Add-Listener-View-Results-Tree-添加察看结果树（）" class="headerlink" title="Add Listener View Results Tree 添加察看结果树（）"></a>Add Listener View Results Tree 添加察看结果树（）</h3><h3 id="Add-Listener-Aggregate-Report-添加聚合报告（）"><a href="#Add-Listener-Aggregate-Report-添加聚合报告（）" class="headerlink" title="Add Listener Aggregate Report 添加聚合报告（）"></a>Add Listener Aggregate Report 添加聚合报告（）</h3><p><strong>聚合报告参数详解</strong></p>
<ol>
<li>Label：每个 <code>JMeter</code> 的 <code>element</code>（例如我这里只有一个 <code>Spring WebFlux</code>）都有一个 <code>Name</code> 属性，这里显示的就是 <code>Name</code> 属性的值。</li>
<li>样本(Samples)：请求数——表示这次测试中一共发出了多少个请求，我这里模拟了<code>100</code>个用户循环<code>6</code>次也就为<code>100*6=600</code>。</li>
<li>平均值(Average)：平均响应时间 (单位:<code>ms</code>)。默认是单个<code>Request</code>的平均响应时间，当使用了<code>Transaction Controller</code>时，也可以是<code>Transaction</code>为单位显示平均响应时间。</li>
<li>中位数(Median)： 50％ 用户的响应时间。</li>
<li>90% 百分位(Line)：90％用户的响应时间。相邻几个<code>*%</code>同意。</li>
<li>最小值(Min)：最小响应时间。</li>
<li>最大值(Max)：最大响应时间。</li>
<li>异常(Error) %：错误率——错误请求数&#x2F;请求总数。</li>
<li>吞吐量(Throughput)：吞吐量——默认情况下表示每秒完成的请求数（<code>Request per Second</code>），当使用了 <code>Transaction Controller</code> 时，也可以表示类似 <code>LoadRunner</code> 的 <code>Transaction per Second</code> 数 。</li>
<li>接收 KB&#x2F;Sec：每秒从服务器端接收到的数据量，相当于<code>LoadRunner</code>中的<code>Throughput/Sec</code>。</li>
<li>发送 KB&#x2F;Sec：每秒向服务器发送的数据量，相当于<code>LoadRunner</code>中的<code>Throughput/Sec</code>。</li>
</ol>
<blockquote>
<p>一般而言，性能测试中我们需要重点关注的数据有： Samples、Average、Min、Max、Error、Throughput。</p>
</blockquote>
<blockquote>
<p><a target="_blank" rel="noopener" href="https://www.jianshu.com/p/6bc152ca6126">Apache JMeter 入门教程</a></p>
<p><a target="_blank" rel="noopener" href="https://baike.baidu.com/item/%E8%BD%AF%E4%BB%B6%E5%8E%8B%E5%8A%9B%E6%B5%8B%E8%AF%95/10364657">软件压力测试</a></p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/11/28/%E7%AE%97%E6%B3%95%E9%A2%98API/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/11/28/%E7%AE%97%E6%B3%95%E9%A2%98API/" class="post-title-link" itemprop="url">算法题C++模板API</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-11-28 22:28:28" itemprop="dateCreated datePublished" datetime="2021-11-28T22:28:28+08:00">2021-11-28</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2022-09-02 22:44:06" itemprop="dateModified" datetime="2022-09-02T22:44:06+08:00">2022-09-02</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/Cpp/" itemprop="url" rel="index"><span itemprop="name">Cpp</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="语法类"><a href="#语法类" class="headerlink" title="语法类"></a>语法类</h1><h2 id="类与结构体"><a href="#类与结构体" class="headerlink" title="类与结构体"></a>类与结构体</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">Node</span> &#123;</span><br><span class="line">    <span class="type">int</span> x, y, z;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">Node</span>(<span class="type">int</span> x, <span class="type">int</span> y, <span class="type">int</span> z) : <span class="built_in">x</span>(x), <span class="built_in">y</span>(y), <span class="built_in">z</span>(z) &#123;&#125;</span><br><span class="line">    </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 初始化</span></span><br><span class="line">    <span class="function">Node <span class="title">node</span><span class="params">(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)</span></span>;</span><br><span class="line">    Node node1 = &#123;<span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>&#125;;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="重新定义类型"><a href="#重新定义类型" class="headerlink" title="重新定义类型"></a>重新定义类型</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> P1 pair<span class="string">&lt;int,int&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> pair&lt;<span class="type">int</span>, <span class="type">int</span>&gt; P2;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">F1</span> &#123;</span><br><span class="line">    <span class="keyword">using</span> P3 = pair&lt;<span class="type">int</span>, <span class="type">int</span>&gt;;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>



<h2 id="IO"><a href="#IO" class="headerlink" title="IO"></a>IO</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> N = <span class="number">100</span>;</span><br><span class="line"><span class="type">char</span> g[N][N];</span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="type">int</span> T;</span><br><span class="line">    <span class="built_in">cin</span> &gt;&gt; T;</span><br><span class="line">    <span class="keyword">while</span> (T--) &#123;</span><br><span class="line">        <span class="type">int</span> m, n;</span><br><span class="line">        <span class="built_in">cin</span> &gt;&gt; m &gt;&gt; n;</span><br><span class="line"><span class="comment">//        memset(g, 0, sizeof(g));</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; m; i++) &#123;</span><br><span class="line">            <span class="built_in">scanf</span>(<span class="string">&quot;%s&quot;</span>, g[i + <span class="number">1</span>] + <span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= m; i++) &#123;</span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">&quot;%s\n&quot;</span>, g[i] + <span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h1 id="algorithm"><a href="#algorithm" class="headerlink" title="algorithm"></a>algorithm</h1><h2 id="reverse"><a href="#reverse" class="headerlink" title="reverse"></a>reverse</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 初始化</span></span><br><span class="line">    vector&lt;<span class="type">int</span>&gt; a = &#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>&#125;;</span><br><span class="line">    <span class="built_in">reverse</span>(a.<span class="built_in">begin</span>(), a.<span class="built_in">end</span>());</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> x : a) &#123;</span><br><span class="line">        cout &lt;&lt; x &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    cout &lt;&lt; endl;</span><br><span class="line">    <span class="type">int</span> b[] = &#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>&#125;;</span><br><span class="line">    <span class="built_in">reverse</span>(b, b + <span class="number">3</span>); <span class="comment">// [)</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> x : b) &#123;</span><br><span class="line">        cout &lt;&lt; x &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h2 id="sort"><a href="#sort" class="headerlink" title="sort"></a>sort</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;algorithm&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">cmp</span><span class="params">(<span class="type">int</span> &amp;a, <span class="type">int</span> &amp;b)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> a % <span class="number">2</span> &lt; b % <span class="number">2</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 对数组排序</span></span><br><span class="line">    <span class="type">int</span> a[<span class="number">4</span>] = &#123;<span class="number">1</span>, <span class="number">3</span>, <span class="number">2</span>, <span class="number">0</span>&#125;;</span><br><span class="line">    <span class="built_in">sort</span>(a, a + <span class="number">4</span>); <span class="comment">// 注意是 [)</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i : a) &#123;</span><br><span class="line">        cout &lt;&lt; i &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    cout &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 从大到小</span></span><br><span class="line">    <span class="built_in">sort</span>(a, a + <span class="number">4</span>, [](<span class="type">int</span> x, <span class="type">int</span> y) &#123;</span><br><span class="line">        <span class="keyword">return</span> x &gt; y;</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i : a) &#123;</span><br><span class="line">        cout &lt;&lt; i &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    cout &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 从大到小</span></span><br><span class="line">    <span class="built_in">sort</span>(a, a + <span class="number">4</span>, cmp); <span class="comment">// 传入 cmp 函数指针</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i : a) &#123;</span><br><span class="line">        cout &lt;&lt; i &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    cout &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// vector&lt;vector&lt;int&gt; &gt; vs;</span></span><br><span class="line">    vector&lt;vector&lt;<span class="type">int</span>&gt;&gt; <span class="built_in">vs</span>(<span class="number">10</span>, <span class="built_in">vector</span>&lt;<span class="type">int</span>&gt;(<span class="number">2</span>)); <span class="comment">// 10 * 2</span></span><br><span class="line">    <span class="built_in">sort</span>(vs.<span class="built_in">begin</span>(), vs.<span class="built_in">end</span>(), [](vector&lt;<span class="type">int</span>&gt; &amp;a, vector&lt;<span class="type">int</span>&gt; &amp;b) &#123;</span><br><span class="line">        <span class="keyword">return</span> a[<span class="number">0</span>] != b[<span class="number">0</span>] ? a[<span class="number">0</span>] &lt; b[<span class="number">0</span>] : a[<span class="number">1</span>] &gt; b[<span class="number">1</span>]; <span class="comment">// 先按第一维从小排序,再第二维从大到小排序</span></span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="comment">// 注意，默认的sort，其实是按vector&lt;int&gt;的字典序排序。</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h2 id="lower-bound、upper-bound"><a href="#lower-bound、upper-bound" class="headerlink" title="lower_bound、upper_bound"></a>lower_bound、upper_bound</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="type">int</span> a[] = &#123;<span class="number">1</span>, <span class="number">3</span>, <span class="number">5</span>, <span class="number">7</span>, <span class="number">9</span>&#125;;</span><br><span class="line">    <span class="built_in">sort</span>(a, a + <span class="number">5</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 返回值 int*</span></span><br><span class="line">    <span class="type">int</span> idx = <span class="built_in">lower_bound</span>(a, a + <span class="number">5</span>, <span class="number">5</span>) - a;</span><br><span class="line">    cout &lt;&lt; idx &lt;&lt; <span class="string">&quot; , &quot;</span> &lt;&lt; a[idx] &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> idx2 = <span class="built_in">upper_bound</span>(a, a + <span class="number">5</span>, <span class="number">5</span>) - a;</span><br><span class="line">    cout &lt;&lt; idx2 &lt;&lt; <span class="string">&quot; , &quot;</span> &lt;&lt; a[idx2] &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    vector&lt;<span class="type">int</span>&gt; v = &#123;<span class="number">2</span>, <span class="number">4</span>, <span class="number">6</span>, <span class="number">8</span>, <span class="number">10</span>&#125;;</span><br><span class="line">    <span class="keyword">auto</span> it1 = <span class="built_in">lower_bound</span>(v.<span class="built_in">begin</span>(), v.<span class="built_in">end</span>(), <span class="number">7</span>);</span><br><span class="line">    <span class="comment">// 返回值是 vector&lt;int&gt;::iterator</span></span><br><span class="line">    vector&lt;<span class="type">int</span>&gt;::iterator it2 = <span class="built_in">lower_bound</span>(v.<span class="built_in">begin</span>(), v.<span class="built_in">end</span>(), <span class="number">7</span>);</span><br><span class="line">    cout &lt;&lt; *it1 &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; *it2 &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> idx3 = <span class="built_in">lower_bound</span>(v.<span class="built_in">begin</span>(), v.<span class="built_in">end</span>(), <span class="number">7</span>) - v.<span class="built_in">begin</span>();</span><br><span class="line">    cout &lt;&lt; idx3 &lt;&lt; <span class="string">&quot; , &quot;</span> &lt;&lt; v[idx3] &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h1 id="data-structure"><a href="#data-structure" class="headerlink" title="data structure"></a>data structure</h1><p>头文件</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;queue&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stack&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;map&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;set&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unordered_map&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unordered_set&gt;</span></span></span><br></pre></td></tr></table></figure>








<h2 id="pair"><a href="#pair" class="headerlink" title="pair"></a>pair</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 初始化方式</span></span><br><span class="line">    P2 p2 = &#123;<span class="number">3</span>, <span class="number">4</span>&#125;;</span><br><span class="line">    <span class="function">P2 <span class="title">p3</span><span class="params">(<span class="number">5</span>, <span class="number">6</span>)</span></span>;</span><br><span class="line">    P2 p4 = <span class="built_in">make_pair</span>(<span class="number">7</span>, <span class="number">8</span>);</span><br><span class="line"></span><br><span class="line">    cout &lt;&lt; p2.first &lt;&lt; <span class="string">&quot;,&quot;</span> &lt;&lt; p2.second &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; p3.first &lt;&lt; <span class="string">&quot;,&quot;</span> &lt;&lt; p3.second &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; p4.first &lt;&lt; <span class="string">&quot;,&quot;</span> &lt;&lt; p4.second &lt;&lt; endl;</span><br><span class="line">   </span><br><span class="line">    vector&lt;P2&gt; v = &#123;&#123;<span class="number">1</span>, <span class="number">5</span>&#125;,</span><br><span class="line">                    &#123;<span class="number">2</span>, <span class="number">4</span>&#125;,</span><br><span class="line">                    &#123;<span class="number">3</span>, <span class="number">3</span>&#125;&#125;;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 解包</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span> [x, y] : v) &#123;</span><br><span class="line">        cout &lt;&lt; x &lt;&lt; <span class="string">&quot;,&quot;</span> &lt;&lt; y;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h2 id="string"><a href="#string" class="headerlink" title="string"></a>string</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    string s1;</span><br><span class="line">    s1.<span class="built_in">push_back</span>(<span class="string">&#x27;a&#x27;</span>);</span><br><span class="line">    s1 += <span class="string">&#x27;b&#x27;</span>;</span><br><span class="line">    s1 += <span class="string">&quot;c&quot;</span>;</span><br><span class="line">    s1.<span class="built_in">append</span>(<span class="string">&quot;def&quot;</span>); <span class="comment">// aka +</span></span><br><span class="line">    cout &lt;&lt; s1 &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 反转</span></span><br><span class="line">    <span class="built_in">reverse</span>(s1.<span class="built_in">begin</span>(), s1.<span class="built_in">end</span>());</span><br><span class="line">    cout &lt;&lt; s1 &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 直接访问</span></span><br><span class="line">    cout &lt;&lt; s1[<span class="number">0</span>] &lt;&lt; <span class="string">&quot; &quot;</span> &lt;&lt; s1[<span class="number">1</span>] &lt;&lt; endl;</span><br><span class="line">   </span><br><span class="line">    <span class="comment">// 子串</span></span><br><span class="line">    string s = <span class="string">&quot;0123456&quot;</span>;</span><br><span class="line">    s1 = s.<span class="built_in">substr</span>(<span class="number">2</span>,<span class="number">4</span>); <span class="comment">// s.substring(2, 2 + 4); aka 2345</span></span><br><span class="line">   </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>字符串和数字互转</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    string s = <span class="string">&quot;123&quot;</span>;</span><br><span class="line">    <span class="type">int</span> x = <span class="built_in">stoi</span>(s);</span><br><span class="line">    cout &lt;&lt; x &lt;&lt; endl;</span><br><span class="line">    string y = <span class="built_in">to_string</span>(x);</span><br><span class="line">    cout &lt;&lt; y &lt;&lt; endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>子串和find</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    string s = <span class="string">&quot;01234567890123456789&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 子串</span></span><br><span class="line">    cout &lt;&lt; s.<span class="built_in">substr</span>() &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; s.<span class="built_in">substr</span>(<span class="number">2</span>) &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; s.<span class="built_in">substr</span>(<span class="number">2</span>, <span class="number">4</span>) &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; s.<span class="built_in">substr</span>(<span class="number">2</span>, <span class="number">100</span>) &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// find、rfind</span></span><br><span class="line">    <span class="type">int</span> p1 = s.<span class="built_in">find</span>(<span class="string">&quot;012&quot;</span>);</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;p1 &quot;</span> &lt;&lt; p1 &lt;&lt; endl;</span><br><span class="line">    <span class="type">int</span> p2 = s.<span class="built_in">find</span>(<span class="string">&quot;012&quot;</span>, <span class="number">1</span>);</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;p2 &quot;</span> &lt;&lt; p2 &lt;&lt; endl;</span><br><span class="line">    <span class="type">int</span> rp = s.<span class="built_in">rfind</span>(<span class="string">&quot;012&quot;</span>);</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;rp &quot;</span> &lt;&lt; rp &lt;&lt; endl;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>split</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="type">char</span> s[] = <span class="string">&quot;a b   c  d e&quot;</span>;</span><br><span class="line">    <span class="type">const</span> <span class="type">char</span>* delim = <span class="string">&quot; &quot;</span>;</span><br><span class="line">    <span class="type">char</span>* p = <span class="literal">nullptr</span>;</span><br><span class="line">    p = <span class="built_in">strtok</span>(s, delim);</span><br><span class="line">    <span class="keyword">while</span> (p) &#123;</span><br><span class="line">        cout &lt;&lt; p &lt;&lt; endl;</span><br><span class="line">        p = <span class="built_in">strtok</span>(<span class="literal">nullptr</span>, delim);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h2 id="vector"><a href="#vector" class="headerlink" title="vector"></a>vector</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 初始化</span></span><br><span class="line">    vector&lt;<span class="type">int</span>&gt; a1;</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">a2</span><span class="params">(<span class="number">10</span>)</span></span>;</span><br><span class="line">    vector&lt;<span class="type">int</span>&gt; a3 = &#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>&#125;;</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">a4</span><span class="params">(<span class="number">100</span>, <span class="number">0x3f3f3f3f</span>)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 清空</span></span><br><span class="line">    a1.<span class="built_in">clear</span>();</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;empty &quot;</span> &lt;&lt; a1.<span class="built_in">empty</span>() &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    a1.<span class="built_in">push_back</span>(<span class="number">1</span>);</span><br><span class="line">    a1.<span class="built_in">push_back</span>(<span class="number">2</span>);</span><br><span class="line">    a1.<span class="built_in">push_back</span>(<span class="number">3</span>); <span class="comment">// 推入</span></span><br><span class="line">    a1.<span class="built_in">pop_back</span>(); <span class="comment">// 弹出</span></span><br><span class="line">    a1.<span class="built_in">push_back</span>(<span class="number">4</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//  原来的值不清空</span></span><br><span class="line">    a1.<span class="built_in">resize</span>(<span class="number">10</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 迭代器 [)</span></span><br><span class="line">    a1.<span class="built_in">begin</span>();</span><br><span class="line">    a1.<span class="built_in">end</span>(); <span class="comment">// 取不到</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 头</span></span><br><span class="line">    cout &lt;&lt; a1.<span class="built_in">front</span>() &lt;&lt; endl;  <span class="comment">// aka a1[0]</span></span><br><span class="line">    <span class="comment">// 尾巴</span></span><br><span class="line">    cout &lt;&lt; a1.<span class="built_in">back</span>() &lt;&lt; endl; <span class="comment">// 可以取到 aka a1[a1.size() - 1]</span></span><br><span class="line"></span><br><span class="line">    a1.<span class="built_in">insert</span>(a1.<span class="built_in">begin</span>() + <span class="number">1</span>, <span class="number">999</span>);</span><br><span class="line"></span><br><span class="line">    cout &lt;&lt; <span class="string">&quot; ---- &quot;</span> &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> x : a1) &#123;</span><br><span class="line">        cout &lt;&lt; x &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    vector&lt;<span class="type">int</span>&gt; a = &#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>&#125;;</span><br><span class="line">    <span class="comment">// 这么写是错的</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span> it = a.<span class="built_in">begin</span>(); it != a.<span class="built_in">end</span>(); ++it) &#123;</span><br><span class="line">        <span class="keyword">if</span> (*it % <span class="number">2</span> == <span class="number">0</span>) &#123;</span><br><span class="line">            a.<span class="built_in">erase</span>(it);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            cout &lt;&lt; *it &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    cout &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> x : a) &#123;</span><br><span class="line">        cout &lt;&lt; x &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;  </span><br></pre></td></tr></table></figure>



<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    vector&lt;<span class="type">int</span>&gt; a = &#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>&#125;;</span><br><span class="line">    <span class="comment">// 正确做法</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span> it = a.<span class="built_in">begin</span>(); it != a.<span class="built_in">end</span>();) &#123;</span><br><span class="line">        <span class="keyword">if</span> (*it % <span class="number">2</span> == <span class="number">0</span>) &#123;</span><br><span class="line">            it = a.<span class="built_in">erase</span>(it); <span class="comment">// 返回下一个有效的iterator</span></span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            cout &lt;&lt; *(it++) &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    cout &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> x : a) &#123;</span><br><span class="line">        cout &lt;&lt; x &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;  </span><br></pre></td></tr></table></figure>





<h2 id="queue、stack"><a href="#queue、stack" class="headerlink" title="queue、stack"></a>queue、stack</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    queue&lt;<span class="type">int</span>&gt; q;</span><br><span class="line">    q.<span class="built_in">push</span>(<span class="number">1</span>);</span><br><span class="line">    q.<span class="built_in">push</span>(<span class="number">2</span>);</span><br><span class="line">    q.<span class="built_in">push</span>(<span class="number">3</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (!q.<span class="built_in">empty</span>()) &#123;</span><br><span class="line">        <span class="type">int</span> x = q.<span class="built_in">front</span>();</span><br><span class="line">        q.<span class="built_in">pop</span>();</span><br><span class="line">        cout &lt;&lt; x &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    cout &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    stack&lt;<span class="type">int</span>&gt; st;</span><br><span class="line">    st.<span class="built_in">push</span>(<span class="number">1</span>);</span><br><span class="line">    st.<span class="built_in">push</span>(<span class="number">2</span>);</span><br><span class="line">    st.<span class="built_in">push</span>(<span class="number">3</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span>(!st.<span class="built_in">empty</span>()) &#123;</span><br><span class="line">        cout &lt;&lt; st.<span class="built_in">top</span>() &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">        st.<span class="built_in">pop</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>




<h2 id="deque"><a href="#deque" class="headerlink" title="deque"></a>deque</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    deque&lt;<span class="type">int</span>&gt; dq;</span><br><span class="line">    </span><br><span class="line">    dq.<span class="built_in">push_back</span>(<span class="number">1</span>);</span><br><span class="line">    dq.<span class="built_in">push_front</span>(<span class="number">2</span>);</span><br><span class="line">    </span><br><span class="line">    dq.<span class="built_in">pop_front</span>();</span><br><span class="line">    dq.<span class="built_in">pop_back</span>();</span><br><span class="line">    </span><br><span class="line">    dq.<span class="built_in">front</span>();</span><br><span class="line">    dq.<span class="built_in">back</span>();</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h2 id="priority-queue"><a href="#priority-queue" class="headerlink" title="priority_queue"></a>priority_queue</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;queue&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Node</span> &#123;</span><br><span class="line">    <span class="type">int</span> x, y;</span><br><span class="line">    <span class="type">bool</span> <span class="keyword">operator</span>&lt;(<span class="type">const</span> Node &amp;node) <span class="type">const</span> &#123; <span class="comment">// 注意两个 const 修饰</span></span><br><span class="line">        <span class="keyword">return</span> x * y &lt; node.x * node.y;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 大根堆</span></span><br><span class="line">    priority_queue&lt;<span class="type">int</span>&gt; pq1;</span><br><span class="line">    <span class="comment">// 小根堆</span></span><br><span class="line">    priority_queue&lt;<span class="type">int</span>, vector&lt;<span class="type">int</span>&gt;, greater&lt;<span class="type">int</span>&gt;&gt; pq2;</span><br><span class="line"></span><br><span class="line">    priority_queue&lt;Node&gt; pq3;</span><br><span class="line">    pq3.<span class="built_in">push</span>(&#123;<span class="number">2</span>, <span class="number">2</span>&#125;);</span><br><span class="line">    pq3.<span class="built_in">push</span>(&#123;<span class="number">3</span>, <span class="number">1</span>&#125;);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span>(pq3.<span class="built_in">size</span>() &gt; <span class="number">0</span>) &#123;</span><br><span class="line">        Node node = pq3.<span class="built_in">top</span>();</span><br><span class="line">        pq3.<span class="built_in">pop</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line"></span><br><span class="line">    <span class="comment">// 不可以既重写Node的操作符，又使用greater</span></span><br><span class="line"><span class="comment">//    priority_queue&lt;Node, vector&lt;Node&gt;, greater&lt;Node&gt;&gt; pq4;</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h2 id="set、unordered-set"><a href="#set、unordered-set" class="headerlink" title="set、unordered_set"></a>set、unordered_set</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    </span><br><span class="line">    set&lt;<span class="type">int</span>&gt; s;</span><br><span class="line"></span><br><span class="line">    s.<span class="built_in">insert</span>(<span class="number">3</span>);</span><br><span class="line">    s.<span class="built_in">insert</span>(<span class="number">2</span>);</span><br><span class="line">    s.<span class="built_in">insert</span>(<span class="number">1</span>);</span><br><span class="line">    s.<span class="built_in">insert</span>(<span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 判断存在与否</span></span><br><span class="line">    <span class="keyword">if</span> (s.<span class="built_in">count</span>(<span class="number">1</span>)) &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (s.<span class="built_in">find</span>(<span class="number">1</span>) != s.<span class="built_in">end</span>()) &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 正序遍历</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> x : s) &#123;</span><br><span class="line">        cout &lt;&lt; x &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    cout &lt;&lt; endl;</span><br><span class="line">    <span class="comment">// 或者</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span> it = s.<span class="built_in">begin</span>(); it != s.<span class="built_in">end</span>(); ++it) &#123;</span><br><span class="line">        cout &lt;&lt; *it &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    cout &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    s.<span class="built_in">erase</span>(<span class="number">2</span>);</span><br><span class="line"></span><br><span class="line">    s.<span class="built_in">erase</span>(s.<span class="built_in">begin</span>());</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 逆序</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span> it = s.<span class="built_in">rbegin</span>(); it != s.<span class="built_in">rend</span>(); ++it) &#123;</span><br><span class="line">        cout &lt;&lt; *it &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    cout &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    cout &lt;&lt; <span class="string">&quot; -------------- &quot;</span> &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 遍历删除的正确做法</span></span><br><span class="line">    s = &#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>&#125;;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span> it = s.<span class="built_in">begin</span>(); it != s.<span class="built_in">end</span>();) &#123;</span><br><span class="line">        <span class="keyword">if</span> (*it % <span class="number">2</span> == <span class="number">0</span>) &#123;</span><br><span class="line">            s.<span class="built_in">erase</span>(it++);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            cout &lt;&lt; *(it++) &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    cout &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> x : s) &#123;</span><br><span class="line">        cout &lt;&lt; x &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    cout &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    s = &#123;<span class="number">1</span>, <span class="number">3</span>, <span class="number">5</span>, <span class="number">7</span>, <span class="number">9</span>&#125;;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 二分查找</span></span><br><span class="line">    <span class="keyword">auto</span> it = s.<span class="built_in">lower_bound</span>(<span class="number">2</span>);</span><br><span class="line">    cout &lt;&lt; *it &lt;&lt; endl;</span><br><span class="line">    <span class="built_in">advance</span>(it, <span class="number">1</span>); <span class="comment">// O(len)</span></span><br><span class="line">    cout &lt;&lt; *it &lt;&lt; endl;</span><br><span class="line">    <span class="built_in">advance</span>(it, <span class="number">2</span>);</span><br><span class="line">    cout &lt;&lt; *it &lt;&lt; endl;</span><br><span class="line">    <span class="built_in">advance</span>(it, <span class="number">1</span>);</span><br><span class="line">    <span class="keyword">if</span> (it == s.<span class="built_in">end</span>()) &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;to end&quot;</span> &lt;&lt; endl;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        cout &lt;&lt; *it &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>unordered_set更简单，因为它是无序的，所以api’更少。</p>
<p><strong>自定义排序类型</strong></p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">Node</span> &#123;</span><br><span class="line">    <span class="type">int</span> x, y;</span><br><span class="line"></span><br><span class="line">    <span class="type">bool</span> <span class="keyword">operator</span>&lt;(<span class="type">const</span> Node &amp;b) <span class="type">const</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> x != b.x ? x &lt; b.x : y &lt; b.y;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    set&lt;Node&gt; set = &#123;&#123;<span class="number">1</span>, <span class="number">2</span>&#125;,</span><br><span class="line">                    &#123;<span class="number">1</span>, <span class="number">1</span>&#125;,</span><br><span class="line">                    &#123;<span class="number">2</span>, <span class="number">0</span>&#125;&#125;;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">auto</span>&amp; node : set) &#123;</span><br><span class="line">        cout &lt;&lt; node.x &lt;&lt;<span class="string">&quot;,&quot;</span> &lt;&lt; node.y &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="map、unordered-map"><a href="#map、unordered-map" class="headerlink" title="map、unordered_map"></a>map、unordered_map</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 初始化</span></span><br><span class="line">    map&lt;<span class="type">int</span>, <span class="type">int</span>&gt; mp;</span><br><span class="line"></span><br><span class="line">    mp = &#123;&#123;<span class="number">1</span>, <span class="number">2</span>&#125;,</span><br><span class="line">          &#123;<span class="number">3</span>, <span class="number">4</span>&#125;&#125;;</span><br><span class="line">    cout &lt;&lt; mp[<span class="number">1</span>]++ &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; mp[<span class="number">1</span>] &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    mp.<span class="built_in">insert</span>(<span class="built_in">pair</span>&lt;<span class="type">int</span>, <span class="type">int</span>&gt;(<span class="number">1</span>, <span class="number">2</span>)); <span class="comment">// 如果键已经存在，则不插入</span></span><br><span class="line">    cout &lt;&lt; mp[<span class="number">1</span>] &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 判断存在与否</span></span><br><span class="line">    <span class="keyword">if</span> (mp.<span class="built_in">find</span>(<span class="number">1</span>) != mp.<span class="built_in">end</span>()) &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (mp.<span class="built_in">count</span>(<span class="number">1</span>) &gt; <span class="number">0</span>) &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    cout &lt;&lt; endl;</span><br><span class="line">    <span class="comment">// 遍历</span></span><br><span class="line">    mp = &#123;&#123;<span class="number">1</span>, <span class="number">2</span>&#125;,</span><br><span class="line">          &#123;<span class="number">3</span>, <span class="number">4</span>&#125;,</span><br><span class="line">          &#123;<span class="number">5</span>, <span class="number">6</span>&#125;,</span><br><span class="line">          &#123;<span class="number">7</span>, <span class="number">8</span>&#125;&#125;;</span><br><span class="line">   	<span class="keyword">for</span>(<span class="keyword">auto</span> [k,v] : mp) &#123;</span><br><span class="line">        cout &lt;&lt; k &lt;&lt; <span class="string">&quot;,&quot;</span> &lt;&lt; v &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span> &amp;item :mp) &#123;</span><br><span class="line">        cout &lt;&lt; item.first &lt;&lt; <span class="string">&quot;,&quot;</span> &lt;&lt; item.second &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">auto</span> it = mp.<span class="built_in">rbegin</span>(); it != mp.<span class="built_in">rend</span>(); it++) &#123;</span><br><span class="line">        cout &lt;&lt; it-&gt;first &lt;&lt; <span class="string">&quot; , &quot;</span> &lt;&lt; it-&gt;second &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h2 id="multiset、multimap"><a href="#multiset、multimap" class="headerlink" title="multiset、multimap"></a>multiset、multimap</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;set&gt;</span></span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    multiset&lt;<span class="type">int</span>&gt; multiset;</span><br><span class="line"></span><br><span class="line">    multiset.<span class="built_in">insert</span>(<span class="number">4</span>);</span><br><span class="line">    multiset.<span class="built_in">insert</span>(<span class="number">1</span>);</span><br><span class="line">    multiset.<span class="built_in">insert</span>(<span class="number">3</span>);</span><br><span class="line">    multiset.<span class="built_in">insert</span>(<span class="number">2</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> x : multiset) &#123;</span><br><span class="line">        cout &lt;&lt; x &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;map&gt;</span></span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    multimap&lt;<span class="type">int</span>, <span class="type">int</span>&gt; mp;</span><br><span class="line">    mp.<span class="built_in">insert</span>(<span class="built_in">make_pair</span>(<span class="number">1</span>, <span class="number">1</span>));</span><br><span class="line">    mp.<span class="built_in">insert</span>(<span class="built_in">make_pair</span>(<span class="number">1</span>, <span class="number">2</span>));</span><br><span class="line">    mp.<span class="built_in">insert</span>(<span class="built_in">make_pair</span>(<span class="number">1</span>, <span class="number">3</span>));</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span> &amp;it:mp) &#123;</span><br><span class="line">        cout &lt;&lt; it.first &lt;&lt; <span class="string">&quot; &quot;</span> &lt;&lt; it.second &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>




<h2 id="容器的相互转换"><a href="#容器的相互转换" class="headerlink" title="容器的相互转换"></a>容器的相互转换</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    deque&lt;<span class="type">int</span>&gt; dq = &#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>&#125;;</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">a</span><span class="params">(dq.begin(), dq.end())</span></span>;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> x : a) &#123;</span><br><span class="line">        cout &lt;&lt; x &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">    a[<span class="number">1</span>] = <span class="number">99</span>; <span class="comment">// 不影响原值</span></span><br><span class="line">    <span class="keyword">while</span> (!dq.<span class="built_in">empty</span>()) &#123;</span><br><span class="line">        cout &lt;&lt; dq.<span class="built_in">front</span>() &lt;&lt; endl;</span><br><span class="line">        dq.<span class="built_in">pop_front</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/11/28/Lambda%E8%A1%A8%E8%BE%BE%E5%BC%8F/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/11/28/Lambda%E8%A1%A8%E8%BE%BE%E5%BC%8F/" class="post-title-link" itemprop="url">Lambda表达式</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-11-28 19:34:21" itemprop="dateCreated datePublished" datetime="2021-11-28T19:34:21+08:00">2021-11-28</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2022-09-02 22:44:06" itemprop="dateModified" datetime="2022-09-02T22:44:06+08:00">2022-09-02</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/Cpp/" itemprop="url" rel="index"><span itemprop="name">Cpp</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="C-中lambda表达式"><a href="#C-中lambda表达式" class="headerlink" title="C++中lambda表达式"></a>C++中lambda表达式</h1><p>Lambda表达式包含一个函数对象参数</p>
<p>标识一个 Lambda 表达式的开始，这部分必须存在，不能省略。函数对象参数是传递给编译器自动生成的函数对象类的构造函数的。函数对象参数只能使用那些到定义 Lambda 为止时 Lambda 所在作用范围内可见的局部变量(包括 Lambda 所在类<br>的 this)。函数对象参数有以下形式：</p>
<ul>
<li>空。没有任何函数对象参数。</li>
<li>&#x3D;。函数体内可以使用 Lambda 所在范围内所有可见的局部变量（包括 Lambda 所在类的 this），并且是值传递方式（相当于编译器自动为我们按值传递了所有局部变量）。</li>
<li>&amp;。函数体内可以使用 Lambda 所在范围内所有可见的局部变量（包括 Lambda 所在类的 this），并且是引用传递方式（相当于是编译器自动为我们按引用传递了所有局部变量）。</li>
<li>this。函数体内可以使用 Lambda 所在类中的成员变量。</li>
</ul>
<p>具体的使用方法示例：</p>
<ul>
<li><code>a</code>。将 a 按值进行传递。按值进行传递时，函数体内不能修改传递进来的 a 的拷贝，因为默认情况下函数是 const 的，要<br>修改传递进来的拷贝，可以添加 mutable 修饰符。</li>
<li><code>&amp;a</code>。将 a 按引用进行传递。</li>
<li><code>a，&amp;b</code>。将 a 按值传递，b 按引用进行传递。</li>
<li><code>[=]，&amp;a，&amp;b</code>。除 a 和 b 按引用进行传递外，其他参数都按值进行传递。</li>
<li><code>[&amp;]，a，b</code>。除 a 和 b 按值进行传递外，其他参数都按引用进行传递</li>
</ul>
<p>sort使用lambda表达式：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    vector&lt;vector&lt;<span class="type">int</span>&gt;&gt; <span class="built_in">allCellsDistOrder</span>(<span class="type">int</span> R, <span class="type">int</span> C, <span class="type">int</span> r0, <span class="type">int</span> c0) &#123;</span><br><span class="line">        vector&lt;vector&lt;<span class="type">int</span>&gt;&gt; ret;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; R; i++) &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j &lt; C; j++) &#123;</span><br><span class="line">                ret.<span class="built_in">push_back</span>(&#123;i, j&#125;);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">sort</span>(ret.<span class="built_in">begin</span>(), ret.<span class="built_in">end</span>(), [=](vector&lt;<span class="type">int</span>&gt;&amp; a, vector&lt;<span class="type">int</span>&gt;&amp; b) &#123;<span class="comment">//因为r0,c0是当前函数的局部变量，所以只能使用值传递而不能使用引用传递造成指针混乱</span></span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">abs</span>(a[<span class="number">0</span>] - r0) + <span class="built_in">abs</span>(a[<span class="number">1</span>] - c0) &lt; <span class="built_in">abs</span>(b[<span class="number">0</span>] - r0) + <span class="built_in">abs</span>(b[<span class="number">1</span>] - c0);</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="keyword">return</span> ret;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/11/27/IPC/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/11/27/IPC/" class="post-title-link" itemprop="url">进程间通信IPC</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-11-27 17:40:03" itemprop="dateCreated datePublished" datetime="2021-11-27T17:40:03+08:00">2021-11-27</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2021-12-15 00:37:43" itemprop="dateModified" datetime="2021-12-15T00:37:43+08:00">2021-12-15</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/" itemprop="url" rel="index"><span itemprop="name">操作系统</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h1><p>进程间通信（IPC：interprocess communication）是一种涉及一个进程与另一个进程的通信的机制。 这通常只发生在一个系统中。</p>
<p>沟通可以有两种类型 -</p>
<ul>
<li>在仅从一个进程启动的相关进程之间，例如父进程和子进程。</li>
<li>在不相关的进程之间，或两个或多个不同的进程之间。</li>
</ul>
<p>具体来说，有下面三大类形式：</p>
<p><img src="/2021/11/27/IPC/ipc.png"></p>
<p>按照《UNIX网络编程 卷2：进程间通信》的目录分类：</p>
<ol>
<li>消息传递（管道、FIFO、消息队列）</li>
<li>同步（互斥锁和条件变量、读写锁、信号量）</li>
<li>共享内存（匿名的、具名的）</li>
</ol>
<h1 id="消息传递"><a href="#消息传递" class="headerlink" title="消息传递"></a>消息传递</h1><h2 id="管道"><a href="#管道" class="headerlink" title="管道"></a>管道</h2><p><strong>管道是两个或多个相关或相互关联的过程之间的通信媒介。</strong></p>
<p>它可以在一个进程内，也可以在子进程和父进程之间进行通信。 通信也可以是多层次的，例如父母，孩子和孙子之间的通信等。通过写入管道的一个过程和管道的其他读取来实现通信。 </p>
<p><strong>要实现管道系统调用，请创建两个文件，一个用于写入文件，另一个用于从文件中读取。</strong></p>
<h3 id="使用方法"><a href="#使用方法" class="headerlink" title="使用方法"></a>使用方法</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="type">int</span> <span class="title function_">pipe</span><span class="params">(<span class="type">int</span> pipedes[<span class="number">2</span>])</span>;</span><br></pre></td></tr></table></figure>

<p>该系统调用将创建用于单向通信的管道，即，它创建两个描述符，第一个连接到管道读取而另一个连接以写入管道。</p>
<p>描述符pipedes [0]用于读取，pipedes [1]用于写入。 无论写入管道[1]是什么都可以从pipedes [0]中读取。</p>
<p>此调用在成功时返回零，在失败时返回-1。 要了解失败的原因，请使用errno variable或perror（）函数进行检查。</p>
<p>如下图这是使用管道进行双向通信的常见做法：</p>
<p><img src="/2021/11/27/IPC/pipe_with_two.jpg" alt="img"></p>
<h3 id="实例代码"><a href="#实例代码" class="headerlink" title="实例代码"></a>实例代码</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="comment">// 用两个单工的管道实现双向通信</span></span><br><span class="line">    <span class="type">int</span> pipefds1[<span class="number">2</span>], pipefds2[<span class="number">2</span>];</span><br><span class="line">    <span class="type">int</span> returnstatus1, returnstatus2;</span><br><span class="line">    <span class="type">int</span> pid;</span><br><span class="line">    <span class="type">char</span> pipe1writemessage[<span class="number">20</span>] = <span class="string">&quot;Hi&quot;</span>;</span><br><span class="line">    <span class="type">char</span> pipe2writemessage[<span class="number">20</span>] = <span class="string">&quot;Hello&quot;</span>;</span><br><span class="line">    <span class="type">char</span> readmessage[<span class="number">20</span>];</span><br><span class="line">    returnstatus1 = pipe(pipefds1);</span><br><span class="line">    <span class="keyword">if</span> (returnstatus1 == <span class="number">-1</span>) &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;Unable to create pipe 1 \n&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    returnstatus2 = pipe(pipefds2);</span><br><span class="line">    <span class="keyword">if</span> (returnstatus2 == <span class="number">-1</span>) &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;Unable to create pipe 2 \n&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    pid = fork();</span><br><span class="line">    <span class="keyword">if</span> (pid != <span class="number">0</span>) &#123; <span class="comment">// Parent process </span></span><br><span class="line">        close(pipefds1[<span class="number">0</span>]); <span class="comment">// Close the unwanted pipe1 read side</span></span><br><span class="line">        close(pipefds2[<span class="number">1</span>]); <span class="comment">// Close the unwanted pipe2 write side</span></span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;In Parent: Writing to pipe 1 – Message is %s\n&quot;</span>, pipe1writemessage);</span><br><span class="line">        write(pipefds1[<span class="number">1</span>], pipe1writemessage, <span class="keyword">sizeof</span>(pipe1writemessage));</span><br><span class="line">        read(pipefds2[<span class="number">0</span>], readmessage, <span class="keyword">sizeof</span>(readmessage));</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;In Parent: Reading from pipe 2 – Message is %s\n&quot;</span>, readmessage);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123; <span class="comment">//child process</span></span><br><span class="line">        close(pipefds1[<span class="number">1</span>]); <span class="comment">// Close the unwanted pipe1 write side</span></span><br><span class="line">        close(pipefds2[<span class="number">0</span>]); <span class="comment">// Close the unwanted pipe2 read side</span></span><br><span class="line">        read(pipefds1[<span class="number">0</span>], readmessage,</span><br><span class="line">             <span class="keyword">sizeof</span>(readmessage));</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;In Child: Reading from pipe 1 – Message is %s\n&quot;</span>, readmessage);</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;In Child: Writing to pipe 2 – Message is %s\n&quot;</span>, pipe2writemessage);</span><br><span class="line">        write(pipefds2[<span class="number">1</span>], pipe2writemessage,</span><br><span class="line">              <span class="keyword">sizeof</span>(pipe2writemessage));</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="FIFO"><a href="#FIFO" class="headerlink" title="FIFO"></a>FIFO</h2><p>暂略</p>
<h2 id="消息队列"><a href="#消息队列" class="headerlink" title="消息队列"></a>消息队列</h2><p>当我们已经拥有共享内存时，为什么我们需要消息队列？ 这将有多种原因，让我们试着将其分解为多个点以简化 -</p>
<ul>
<li>如所理解的，一<strong>旦进程接收到消息，它将不再可用于任何其他过程</strong>。 而在共享内存中，数据可供多个进程访问。</li>
<li>如果我们想要与小消息格式进行通信。</li>
<li>当多个进程同时通信时，<strong>共享内存数据需要通过同步进行保护</strong>。</li>
<li>使用共享内存的写入和读取频率很高，但<strong>实现该功能将非常复杂</strong>。 在这种情况下使用不值得。</li>
<li>如果<strong>并不是所有进程都需要访问共享内存</strong>，那么使用消息队列实现会更好。</li>
<li>如果我们想要<strong>与不同的数据包进行通信</strong>，则说过程A将消息类型1发送到进程B，消息类型10发送到进程C，消息类型20发送到进程D.在这种情况下，使用消息队列实现更简单。 为了将给定的消息类型简化为1,10,20，它可以是0或+ ve或-ve，如下所述。</li>
<li>当然，<strong>消息队列的顺序是FIFO</strong>（先进先出）。 插入队列中的第一条消息是第一个要检索的消息。</li>
</ul>
<ol>
<li>通过一个进程写入共享内存并通过另一个进程从共享内存中读取。 我们知道，阅读也可以通过多个流程完成</li>
</ol>
<p><img src="/2021/11/27/IPC/message_queue.jpg" alt="消息队列"></p>
<ol start="2">
<li>一个进程使用不同的数据包写入共享内存，并通过多个进程从中读取，即按消息类型。</li>
</ol>
<p><img src="/2021/11/27/IPC/multiple_message_queue.jpg" alt="多个消息队列"></p>
<h3 id="使用方法-1"><a href="#使用方法-1" class="headerlink" title="使用方法"></a>使用方法</h3><p>这里以POSIX的接口为实例</p>
<table>
<thead>
<tr>
<th>API</th>
<th>API 作用</th>
</tr>
</thead>
<tbody><tr>
<td>mqd_t mq_open(const char <em>name, int oflag, mode_t mode, struct mq_attr</em> attr)</td>
<td>创建命名消息队列</td>
</tr>
<tr>
<td>mqd_t mq_close(mqd_t mqdes)</td>
<td>结束到开放式消息队列的连接</td>
</tr>
<tr>
<td>mqd_t mq_unlink(const char *name)</td>
<td>结束到开放式消息队列的连接，并在最后一个进程关闭此队列时将其删除</td>
</tr>
<tr>
<td>mqd_t mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio)</td>
<td>将消息放入队列</td>
</tr>
<tr>
<td>ssize_t mq_receive(mqd_t mqdes, char <em>msg_ptr, size_t msg_len, unsigned</em> msg_prio)</td>
<td>在队列中接收消息</td>
</tr>
<tr>
<td>mqd_t mq_notify(mqd_t mqdes, const struct sigevent *notification)</td>
<td>通知进程或线程消息已存在于队列中</td>
</tr>
<tr>
<td>mqd_t mq_getattr(mqd_t mqdes, struct mq_attr <em>attr) 、mqd_t mq_setattr(mqd_t mqdes, struct mq_attr</em> newattr, struct mq_attr *oldattr)</td>
<td>设置或获取消息队列属性</td>
</tr>
</tbody></table>
<h1 id="同步"><a href="#同步" class="headerlink" title="同步"></a>同步</h1><h2 id="互斥锁与条件变量"><a href="#互斥锁与条件变量" class="headerlink" title="互斥锁与条件变量"></a>互斥锁与条件变量</h2><p>这里互斥锁和条件变量的使用，与Java的<code>synchronized+wait+notify</code>以及<code>Lock+Condition</code> 机制在语义上和使用上十分相似，就不详细介绍了。</p>
<h3 id="使用方法-2"><a href="#使用方法-2" class="headerlink" title="使用方法"></a>使用方法</h3><blockquote>
<p>linux c版本的条件变量和互斥锁(mutex)，不是C++的。</p>
</blockquote>
<ol>
<li>互斥锁的初始化</li>
</ol>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">pthread_mutex_init</span><span class="params">(<span class="type">pthread_mutex_t</span> *<span class="keyword">restrict</span> mutex, <span class="type">const</span> <span class="type">pthread_mutexattr_t</span> *<span class="keyword">restrict</span> attr)</span>;</span><br><span class="line"><span class="type">pthread_mutex_t</span> mutex = PTHREAD_MUTEX_INITIALIZER;</span><br></pre></td></tr></table></figure>

<ol start="2">
<li>互斥锁的销毁</li>
</ol>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">pthread_mutex_destroy</span><span class="params">(<span class="type">pthread_mutex_t</span> *mutex)</span>;</span><br></pre></td></tr></table></figure>



<ol start="3">
<li>加锁与解锁</li>
</ol>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">pthread_mutex_lock</span><span class="params">(<span class="type">pthread_mutex_t</span> *mutex)</span>;</span><br><span class="line"><span class="type">int</span> <span class="title function_">pthread_mutex_trylock</span><span class="params">(<span class="type">pthread_mutex_t</span> *mutex)</span>;</span><br><span class="line"><span class="type">int</span> <span class="title function_">pthread_mutex_unlock</span><span class="params">(<span class="type">pthread_mutex_t</span> *mutex)</span>;</span><br></pre></td></tr></table></figure>



<ol start="4">
<li>条件变量的函数</li>
</ol>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">pthread_cond_wait</span><span class="params">(<span class="type">pthread_cond_t</span> *<span class="keyword">restrict</span> cond,</span></span><br><span class="line"><span class="params">           <span class="type">pthread_mutex_t</span> *<span class="keyword">restrict</span> mutex)</span>;</span><br><span class="line"><span class="type">int</span> <span class="title function_">pthread_cond_signal</span><span class="params">(<span class="type">pthread_cond_t</span> *cond)</span>;</span><br></pre></td></tr></table></figure>





<h2 id="信号量"><a href="#信号量" class="headerlink" title="信号量"></a>信号量</h2><p>基本上信号量分为两种类型 -</p>
<p><strong>Binary Semaphores</strong> - 只有两个状态0和1，即锁定&#x2F;解锁或可用&#x2F;不可用，Mutex实现。</p>
<p><strong>Counting Semaphores</strong> - 允许任意资源计数的信号量称为计数信号量。</p>
<h3 id="使用方法-3"><a href="#使用方法-3" class="headerlink" title="使用方法"></a>使用方法</h3><table>
<thead>
<tr>
<th>API</th>
<th>API 作用</th>
</tr>
</thead>
<tbody><tr>
<td>sem_t *sem_open(const char  *name, int oflag, mode_t mode, unsigned int value)</td>
<td>创建命名信号量</td>
</tr>
<tr>
<td>int sem_init(sem_t *sem, int pshared, unsigned int value)</td>
<td>初始化信号量结构</td>
</tr>
<tr>
<td>int sem_close(sem_t *sem)</td>
<td>结束到开放式信号量的连接</td>
</tr>
<tr>
<td>int sem_unlink(const char *name)</td>
<td>结束到开放式信号量的连接，并在最后一个进程关闭此信号量时将其删除</td>
</tr>
<tr>
<td>int sem_getvalue(sem_t <em>sem, int</em> sval)</td>
<td>将信号量的值复制到指定整数中</td>
</tr>
<tr>
<td>int sem_wait(sem_t *sem)</td>
<td>递减信号量计数，当其他进程拥有信号量时进行阻塞，或者当其他进程拥有信号量时返回错误（aka P 操作）</td>
</tr>
<tr>
<td>int sem_post(sem_t *sem)</td>
<td>递增信号量计数（aka V 操作）</td>
</tr>
</tbody></table>
<h1 id="共享内存相关"><a href="#共享内存相关" class="headerlink" title="共享内存相关"></a>共享内存相关</h1><h2 id="内存映射"><a href="#内存映射" class="headerlink" title="内存映射"></a>内存映射</h2><p>mmap()系统调用在调用进程的虚拟地址空间中提供映射，该映射将文件或设备映射到内存中。 这有两种类型 -</p>
<p><strong>File mapping or File-backed mapping</strong> - 此映射将进程的虚拟内存区域映射到文件。 这意味着读取或写入这些内存区域会导致文件被读取或写入。 这是默认的映射类型。</p>
<p><strong>Anonymous mapping</strong> - 此映射映射进程的虚拟内存区域，不受任何文件的支持。 内容初始化为零。 此映射类似于动态内存分配（malloc()），并在某些malloc()实现中用于某些分配。</p>
<p>当两个或多个进程共享相同的页面时，每个进程可以根据映射类型查看其他进程所做的页面内容的更改。 映射类型可以是私有的也可以是共享的 -</p>
<p><strong>Private Mapping (MAP_PRIVATE)</strong> - 对此映射内容的修改对其他进程不可见，并且映射不会传送到基础文件。</p>
<p><strong>Shared Mapping (MAP_SHARED)</strong> - 对此映射内容的修改对其他进程可见，并且映射将传送到基础文件。</p>
<h3 id="使用方法-4"><a href="#使用方法-4" class="headerlink" title="使用方法"></a>使用方法</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/mman.h&gt;</span></span></span><br><span class="line"><span class="type">void</span> *<span class="title function_">mmap</span><span class="params">(<span class="type">void</span> *addr, <span class="type">size_t</span> length, <span class="type">int</span> prot, <span class="type">int</span> flags, <span class="type">int</span> fd, <span class="type">off_t</span> offset)</span>;</span><br></pre></td></tr></table></figure>

<ul>
<li>参数</li>
</ul>
<p>虚拟地址addr可以是用户指定的，也可以是内核生成的（将addr作为NULL传递）;</p>
<p>字段length需要以字节为单位的映射大小;</p>
<p>字段prot表示存储器保护值，例如PROT_NONE，PROT_READ，PROT_WRITE，PROT_EXEC，分别用于可能无法访问，读取，写入或执行的区域。 该值可以是单个（PROT_NONE），也可以与三个标志中的任何一个（最后3个）进行OR运算;</p>
<p>字段flags指示映射类型或MAP_PRIVATE或MAP_SHARED;</p>
<p>字段fd表示标识要映射的文件的文件描述符;</p>
<p>字段offset表示文件的起始点，如果需要映射整个文件，则偏移量应为零。</p>
<ul>
<li>返回值</li>
</ul>
<p>在成功时返回映射的起始地址，或者在出错时返回MAP_FAILED。</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/mman.h&gt;</span></span></span><br><span class="line"><span class="type">int</span> <span class="title function_">munmap</span><span class="params">(<span class="type">void</span> *addr, <span class="type">size_t</span> length)</span>;</span><br></pre></td></tr></table></figure>

<p>执行已映射内存映射区域的取消映射。</p>
<p>字段addr表示映射的起始地址，length表示要取消映射的映射的字节大小。</p>
<h3 id="实例代码-1"><a href="#实例代码-1" class="headerlink" title="实例代码"></a>实例代码</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Filename: mmap_test.c */</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/stat.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/types.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;fcntl.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/mman.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">write_mmap_sample_data</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">stat</span> <span class="title">mmapstat</span>;</span></span><br><span class="line">    <span class="type">char</span> *data;</span><br><span class="line">    <span class="type">int</span> minbyteindex;</span><br><span class="line">    <span class="type">int</span> maxbyteindex;</span><br><span class="line">    <span class="type">int</span> offset;</span><br><span class="line">    <span class="type">int</span> fd;</span><br><span class="line">    <span class="type">int</span> unmapstatus;</span><br><span class="line">    write_mmap_sample_data();</span><br><span class="line">    <span class="keyword">if</span> (stat(<span class="string">&quot;MMAP_DATA.txt&quot;</span>, &amp;mmapstat) == <span class="number">-1</span>) &#123;</span><br><span class="line">        perror(<span class="string">&quot;stat failure&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> ((fd = open(<span class="string">&quot;MMAP_DATA.txt&quot;</span>, O_RDONLY)) == <span class="number">-1</span>) &#123;</span><br><span class="line">        perror(<span class="string">&quot;open failure&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    data = mmap((<span class="type">caddr_t</span>) <span class="number">0</span>, mmapstat.st_size, PROT_READ, MAP_SHARED, fd, <span class="number">0</span>);</span><br><span class="line">    <span class="keyword">if</span> (data == (<span class="type">caddr_t</span>) (<span class="number">-1</span>)) &#123;</span><br><span class="line">        perror(<span class="string">&quot;mmap failure&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    minbyteindex = <span class="number">0</span>;</span><br><span class="line">    maxbyteindex = mmapstat.st_size - <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">do</span> &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;Enter -1 to quit or &quot;</span>);</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;enter a number between %d and %d: &quot;</span>, minbyteindex, maxbyteindex);</span><br><span class="line">        <span class="built_in">scanf</span>(<span class="string">&quot;%d&quot;</span>, &amp;offset);</span><br><span class="line">        <span class="keyword">if</span> ((offset &gt;= <span class="number">0</span>) &amp;&amp; (offset &lt;= maxbyteindex))</span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">&quot;Received char at %d is %c\n&quot;</span>, offset, data[offset]);</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (offset != <span class="number">-1</span>)</span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">&quot;Received invalid index %d\n&quot;</span>, offset);</span><br><span class="line">    &#125; <span class="keyword">while</span> (offset != <span class="number">-1</span>);</span><br><span class="line">    unmapstatus = munmap(data, mmapstat.st_size);</span><br><span class="line">    <span class="keyword">if</span> (unmapstatus == <span class="number">-1</span>) &#123;</span><br><span class="line">        perror(<span class="string">&quot;munmap failure&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    close(fd);</span><br><span class="line">    system(<span class="string">&quot;rm -f MMAP_DATA.txt&quot;</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">write_mmap_sample_data</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="type">int</span> fd;</span><br><span class="line">    <span class="type">char</span> ch;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">stat</span> <span class="title">textfilestat</span>;</span></span><br><span class="line">    fd = open(<span class="string">&quot;MMAP_DATA.txt&quot;</span>, O_CREAT | O_TRUNC | O_WRONLY, <span class="number">0666</span>);</span><br><span class="line">    <span class="keyword">if</span> (fd == <span class="number">-1</span>) &#123;</span><br><span class="line">        perror(<span class="string">&quot;File open error &quot;</span>);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// Write A to Z</span></span><br><span class="line">    ch = <span class="string">&#x27;A&#x27;</span>;</span><br><span class="line">    <span class="keyword">while</span> (ch &lt;= <span class="string">&#x27;Z&#x27;</span>) &#123;</span><br><span class="line">        write(fd, &amp;ch, <span class="keyword">sizeof</span>(ch));</span><br><span class="line">        ch++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// Write 0 to 9</span></span><br><span class="line">    ch = <span class="string">&#x27;0&#x27;</span>;</span><br><span class="line">    <span class="keyword">while</span> (ch &lt;= <span class="string">&#x27;9&#x27;</span>) &#123;</span><br><span class="line">        write(fd, &amp;ch, <span class="keyword">sizeof</span>(ch));</span><br><span class="line">        ch++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// Write a to z</span></span><br><span class="line">    ch = <span class="string">&#x27;a&#x27;</span>;</span><br><span class="line">    <span class="keyword">while</span> (ch &lt;= <span class="string">&#x27;z&#x27;</span>) &#123;</span><br><span class="line">        write(fd, &amp;ch, <span class="keyword">sizeof</span>(ch));</span><br><span class="line">        ch++;</span><br><span class="line">    &#125;</span><br><span class="line">    close(fd);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h2 id="共享内存"><a href="#共享内存" class="headerlink" title="共享内存"></a>共享内存</h2><p>共享内存是两个或多个进程之间共享的内存。 但是，为什么我们需要共享内存或其他一些通信方式？</p>
<p>重申一下，每个进程都有自己的地址空间，如果任何进程想要将自己的地址空间中的某些信息与其他进程通信，那么只有IPC（进程间通信）技术才有可能。 我们已经知道，沟通可以在相关或不相关的流程之间进行。</p>
<h3 id="Posix-版本"><a href="#Posix-版本" class="headerlink" title="Posix 版本"></a>Posix 版本</h3><table>
<thead>
<tr>
<th>API</th>
<th>API 作用</th>
</tr>
</thead>
<tbody><tr>
<td>int shm_open(const char *name, int oflag, mode_t mode)</td>
<td>创建共享内存，成功返回描述符</td>
</tr>
<tr>
<td>int shm_unlink(const char *name)</td>
<td>结束到共享内存的连接，并在最后一个进程关闭它时将其删除</td>
</tr>
<tr>
<td>void <em>mmap(void</em> addr, size_t length, int prot, int flags, int fd, off_t offset)</td>
<td>映射内存</td>
</tr>
</tbody></table>
<p>Posix的共享内存区构筑在<code>mmap</code>之上。</p>
<h3 id="SystemV-版本"><a href="#SystemV-版本" class="headerlink" title="SystemV 版本"></a>SystemV 版本</h3><p>相关函数</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/ipc.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/shm.h&gt;</span></span></span><br><span class="line"><span class="comment">// 创建或分配System V共享内存段, 成功时返回有效的共享内存标识符（用于进一步调用共享内存）</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">shmget</span><span class="params">(<span class="type">key_t</span> key, <span class="type">size_t</span> size, <span class="type">int</span> shmflg)</span></span><br><span class="line"><span class="comment">// 为System V共享内存段执行共享内存操作，即将共享内存段附加到调用进程的地址空间。成功时返回附加共享内存段的地址。 </span></span><br><span class="line"><span class="type">void</span> * <span class="title function_">shmat</span><span class="params">(<span class="type">int</span> shmid, <span class="type">const</span> <span class="type">void</span> *shmaddr, <span class="type">int</span> shmflg)</span></span><br><span class="line"><span class="comment">// 从调用进程的地址空间中分离共享内存段的System V共享内存段。将在成功时返回0，在失败时返回-1</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">shmdt</span><span class="params">(<span class="type">const</span> <span class="type">void</span> *shmaddr)</span></span><br><span class="line"><span class="comment">// 执行System V共享内存段的控制操作</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">shmctl</span><span class="params">(<span class="type">int</span> shmid, <span class="type">int</span> cmd, <span class="keyword">struct</span> shmid_ds *buf)</span>  </span><br></pre></td></tr></table></figure>





<blockquote>
<p>参考链接</p>
<p>《UNIX网络编程 卷2：进程间通信》</p>
<p><a target="_blank" rel="noopener" href="https://iowiki.com/inter_process_communication/inter_process_communication_index.html">WIKI教程</a><br><a target="_blank" rel="noopener" href="https://www.cnblogs.com/52php/p/5861372.html">Linux进程间通信（六）：共享内存 shmget()、shmat()、shmdt()、shmctl() </a></p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/11/27/Linux%E5%91%BD%E4%BB%A4-%E2%80%94%E2%80%94-%E8%BF%9B%E7%A8%8B%E3%80%81%E7%BA%BF%E7%A8%8B%E7%9B%91%E6%8E%A7/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/11/27/Linux%E5%91%BD%E4%BB%A4-%E2%80%94%E2%80%94-%E8%BF%9B%E7%A8%8B%E3%80%81%E7%BA%BF%E7%A8%8B%E7%9B%91%E6%8E%A7/" class="post-title-link" itemprop="url">Linux命令 —— 进程、线程监控</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-11-27 13:02:16" itemprop="dateCreated datePublished" datetime="2021-11-27T13:02:16+08:00">2021-11-27</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2022-03-28 15:28:16" itemprop="dateModified" datetime="2022-03-28T15:28:16+08:00">2022-03-28</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/Linux/" itemprop="url" rel="index"><span itemprop="name">Linux</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          
      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/11/26/HTTP/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/11/26/HTTP/" class="post-title-link" itemprop="url">HTTP协议</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-11-26 08:33:48" itemprop="dateCreated datePublished" datetime="2021-11-26T08:33:48+08:00">2021-11-26</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-01-04 01:32:41" itemprop="dateModified" datetime="2023-01-04T01:32:41+08:00">2023-01-04</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/" itemprop="url" rel="index"><span itemprop="name">计算机网络</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <p>一个不错的网站可以学习：</p>
<p><a target="_blank" rel="noopener" href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP">https://developer.mozilla.org/zh-CN/docs/Web/HTTP</a></p>
<h1 id="HTTP"><a href="#HTTP" class="headerlink" title="HTTP"></a>HTTP</h1><p><strong>一个HTTP请求报文由四个部分组成：请求行、请求头部、空行、请求数据。</strong></p>
<h2 id="HTTP头部"><a href="#HTTP头部" class="headerlink" title="HTTP头部"></a>HTTP头部</h2><p>参考文章：</p>
<p><a target="_blank" rel="noopener" href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers">https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers</a></p>
<p>HTTP 头部本质上是一个传递额外重要信息的键值对。</p>
<p>主要分为：通用头部，请求头部，响应头部和实体头部。</p>
<ol>
<li>通用头：是客户端和服务器都可以使用的头部，可以在客户端、服务器和其他应用程序之间提供一些非常有用的通用功能，如Date头部。</li>
<li>请求头：请求报文特有的，它们为服务器提供了一些额外信息，比如客户端希望接收什么类型的数据，如Accept头部。</li>
<li>响应头：响应报文特有的，比如，客服端在与哪种类型的服务器进行交互，如Server头部。</li>
<li>实体头：实体首部字段是包含在请求报文和响应报文中的实体部分使用的首部，用于补充内容的更新时间等与实体相关的信息。</li>
</ol>
<h3 id="通用头部"><a href="#通用头部" class="headerlink" title="通用头部"></a>通用头部</h3><table>
<thead>
<tr>
<th>协议头</th>
<th>说明</th>
<th>举例</th>
</tr>
</thead>
<tbody>
<tr>
<td>Cache-Control</td>
<td>用来指定当前的请求/回复中是否使用缓存机制</td>
<td>Cache-Control: no-store</td>
</tr>
<tr>
<td>Connection</td>
<td>客户端（浏览器）想要优先使用的连接类型</td>
<td>Connection: keep-alive (Upgrade)</td>
</tr>
<tr>
<td>Date</td>
<td>报文创建时间</td>
<td>Date: Dec, 26 Dec 2015 17: 30: 00 GMT</td>
</tr>
<tr>
<td>Trailer</td>
<td>会实现说明在报文主体后记录哪些首部字段，该首部字段可以使用在 HTTP/1.1 版本分块传输编码时</td>
<td>Trailer: Expiress</td>
</tr>
<tr>
<td>Transfer-Encoding</td>
<td>用来改变报文格式</td>
<td>Transfer-Encoding: chunked</td>
</tr>
<tr>
<td>Upgrade</td>
<td>要求服务器升级到一个高版本协议</td>
<td>Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11</td>
</tr>
<tr>
<td>Via</td>
<td>告诉服务器，这个请求是由哪些代理发出的</td>
<td>Via: 1.0 fred, 1.1 <a href="http://itbilu.com.com" target="_blank">itbilu.com.com</a> (Apache/1.1)</td>
</tr>
<tr>
<td>Warning</td>
<td>一个一般性的警告，表示在实体内容中可能存在错误</td>
<td>Warning: 199 Miscellaneous warning</td>
</tr>
</tbody>
</table>



<p>比较重要的有 <code>Cache-Control</code>、<code>Connection</code>。</p>
<h3 id="请求头部"><a href="#请求头部" class="headerlink" title="请求头部"></a>请求头部</h3><table>
<thead>
<tr>
<th>协议头</th>
<th>说明</th>
<th>举例</th>
</tr>
</thead>
<tbody>
<tr>
<td>Accept</td>
<td>告诉服务器自己允许哪些媒体类型</td>
<td>Accept: text/plain</td>
</tr>
<tr>
<td>Accept-Charset</td>
<td>浏览器申明可接受的字符集</td>
<td>Accept-Charset: utf-8</td>
</tr>
<tr>
<td>Accept-Encoding</td>
<td>浏览器申明自己接收的编码方法</td>
<td>Accept-Encoding: gzip, deflate</td>
</tr>
<tr>
<td>Accept-Language</td>
<td>浏览器可接受的响应内容语言列表</td>
<td>Accept-Language: en-US</td>
</tr>
<tr>
<td>Authorization</td>
<td>用于表示 HTTP 协议中需要认证资源的认证信息</td>
<td>Authorization: Basic OSdjJGRpbjpvcGVul ANIc2SdDE==</td>
</tr>
<tr>
<td>Expect</td>
<td>表示客户端要求服务器做出特定的行为</td>
<td>Expect: 100-continue</td>
</tr>
<tr>
<td>From</td>
<td>发起此请求的用户的邮件地址</td>
<td>From: <a href="mailto:user@itbilu.com" target="_blank">user@itbilu.com</a></td>
</tr>
<tr>
<td>Host</td>
<td>表示服务器的域名以及服务器所监听的端口号</td>
<td>Host: <a href="http://www.itbilu.com:80" target="_blank">www.itbilu.com:80</a></td>
</tr>
<tr>
<td>If-XXX</td>
<td>条件请求</td>
<td>If-Modified-Since: Dec, 26 Dec 2015 17:30:00 GMT</td>
</tr>
<tr>
<td>Max-Forwards</td>
<td>限制该消息可被代理及网关转发的次数</td>
<td>Max-Forwards: 10</td>
</tr>
<tr>
<td>Range</td>
<td>表示请求某个实体的一部分，字节偏移以 0 开始</td>
<td>Range: bytes=500-999</td>
</tr>
<tr>
<td>Referer</td>
<td>表示浏览器所访问的前一个页面，可以认为是之前访问页面的链接将浏览器带到了当前页面</td>
<td>Referer: <a href="http://itbilu.com/nodejs" target="_blank">http://itbilu.com/nodejs</a></td>
</tr>
<tr>
<td>User-Agent</td>
<td>浏览器的身份标识字符串</td>
<td>User-Agent: Mozilla/……</td>
</tr>
</tbody>
</table>


<p>比较重要的有<code>Referer</code>、<code>User-Agent</code>、<code>Range</code>。</p>
<h3 id="响应头部"><a href="#响应头部" class="headerlink" title="响应头部"></a>响应头部</h3><table>
<thead>
<tr>
<th>协议头</th>
<th>说明</th>
<th>举例</th>
</tr>
</thead>
<tbody>
<tr>
<td>Accept-Ranges</td>
<td>字段的值表示可用于定义范围的单位</td>
<td>Accept-Ranges: bytes</td>
</tr>
<tr>
<td>Age</td>
<td>创建响应的时间</td>
<td>Age：5744337</td>
</tr>
<tr>
<td>ETag</td>
<td>唯一标识分配的资源</td>
<td>Etag：W/"585cd998-7c0f"</td>
</tr>
<tr>
<td>Location</td>
<td>表示重定向后的 URL</td>
<td>Location: <a href="http://www.zcmhi.com/archives/94.html" target="_blank">http://www.zcmhi.com/archives/94.html</a></td>
</tr>
<tr>
<td>Retry-After</td>
<td>告知客户端多久后再发送请求</td>
<td>Retry-After: 120</td>
</tr>
<tr>
<td>Server</td>
<td>告知客户端服务器信息</td>
<td>Server: Apache/1.3.27 (Unix) (Red-Hat/Linux)</td>
</tr>
<tr>
<td>Vary</td>
<td>缓存控制</td>
<td>Vary: Origin</td>
</tr>
</tbody>
</table>


<p>比较重要的有<code>Accept-Ranges</code>、<code>Location</code></p>
<h3 id="实体头部"><a href="#实体头部" class="headerlink" title="实体头部"></a>实体头部</h3><table>
<thead>
<tr>
<th>协议头</th>
<th>说明</th>
<th>举例</th>
</tr>
</thead>
<tbody>
<tr>
<td>Allow</td>
<td>对某网络资源的有效的请求行为，不允许则返回405</td>
<td>Allow: GET, HEAD</td>
</tr>
<tr>
<td>Content-encoding</td>
<td>返回内容的编码方式</td>
<td>Content-Encoding: gzip</td>
</tr>
<tr>
<td>Content-Length</td>
<td>返回内容的字节长度</td>
<td>Content-Length: 348</td>
</tr>
<tr>
<td>Content-Language</td>
<td>响应体的语言</td>
<td>Content-Language: en,zh</td>
</tr>
<tr>
<td>Content-Location</td>
<td>请求资源可替代的备用的另一地址</td>
<td>Content-Location: /index.htm</td>
</tr>
<tr>
<td>Content-MD5</td>
<td>返回资源的MD5校验值</td>
<td>Content-MD5: Q2hlY2sgSW50ZWdyaXR5IQ==</td>
</tr>
<tr>
<td>Content-Range</td>
<td>在整个返回体中本部分的字节位置</td>
<td>Content-Range: bytes 21010-47021/47022</td>
</tr>
<tr>
<td>Content-Type</td>
<td>返回内容的MIME类型</td>
<td>Content-Type: text/html; charset=utf-8</td>
</tr>
<tr>
<td>Expires</td>
<td>响应过期的日期和时间</td>
<td>Expires: Thu, 01 Dec 2010 16:00:00 GMT</td>
</tr>
<tr>
<td>Last-Modified</td>
<td>请求资源的最后修改时间</td>
<td>Last-Modified: Tue, 15 Nov 2010 12:45:26 GMT</td>
</tr>
</tbody>
</table>


<p>比较重要的有<code>Content-encoding</code>、<code>Content-Length</code>、<code>Content-Type</code>、<code>Content-MD5</code>、<code>Content-Range</code></p>
<h2 id="常用头部"><a href="#常用头部" class="headerlink" title="常用头部"></a>常用头部</h2><p>从hertz的<code>pkg/protocol/consts/headers.go</code> 里面列出了bytedancer认为的一些比较常用的header。</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> (</span><br><span class="line">	HeaderAuthorization = <span class="string">&quot;Authorization&quot;</span></span><br><span class="line">	HeaderHost          = <span class="string">&quot;Host&quot;</span></span><br><span class="line">	HeaderReferer       = <span class="string">&quot;Referer&quot;</span></span><br><span class="line">	HeaderContentType   = <span class="string">&quot;Content-Type&quot;</span></span><br><span class="line">	HeaderUserAgent     = <span class="string">&quot;User-Agent&quot;</span></span><br><span class="line">	HeaderExpect        = <span class="string">&quot;Expect&quot;</span></span><br><span class="line">	HeaderConnection    = <span class="string">&quot;Connection&quot;</span></span><br><span class="line">	HeaderContentLength = <span class="string">&quot;Content-Length&quot;</span></span><br><span class="line">	HeaderCookie        = <span class="string">&quot;Cookie&quot;</span></span><br><span class="line"></span><br><span class="line">	HeaderServer           = <span class="string">&quot;Server&quot;</span></span><br><span class="line">	HeaderServerLower      = <span class="string">&quot;server&quot;</span></span><br><span class="line">	HeaderSetCookie        = <span class="string">&quot;Set-Cookie&quot;</span></span><br><span class="line">	HeaderSetCookieLower   = <span class="string">&quot;set-cookie&quot;</span></span><br><span class="line">	HeaderTransferEncoding = <span class="string">&quot;Transfer-Encoding&quot;</span></span><br><span class="line">	HeaderDate             = <span class="string">&quot;Date&quot;</span></span><br><span class="line"></span><br><span class="line">	HeaderRange        = <span class="string">&quot;Range&quot;</span></span><br><span class="line">	HeaderAcceptRanges = <span class="string">&quot;Accept-Ranges&quot;</span></span><br><span class="line">	HeaderContentRange = <span class="string">&quot;Content-Range&quot;</span></span><br><span class="line"></span><br><span class="line">	HeaderIfModifiedSince = <span class="string">&quot;If-Modified-Since&quot;</span></span><br><span class="line">	HeaderLastModified    = <span class="string">&quot;Last-Modified&quot;</span></span><br><span class="line"></span><br><span class="line">	<span class="comment">// Message body information</span></span><br><span class="line">	HeaderContentEncoding = <span class="string">&quot;Content-Encoding&quot;</span></span><br><span class="line">	HeaderAcceptEncoding  = <span class="string">&quot;Accept-Encoding&quot;</span></span><br><span class="line"></span><br><span class="line">	<span class="comment">// Redirects</span></span><br><span class="line">	HeaderLocation = <span class="string">&quot;Location&quot;</span></span><br><span class="line">  </span><br><span class="line">  <span class="comment">// ... </span></span><br><span class="line">)</span><br><span class="line"></span><br></pre></td></tr></table></figure>







<h2 id="HTTP状态码"><a href="#HTTP状态码" class="headerlink" title="HTTP状态码"></a>HTTP状态码</h2><p>HTTP 状态码由三个十进制数字组成，第一个数字定义了状态码的类型，后两个并没有起到分类的作用。HTTP 状态码共有 5 种类型：</p>
<table>
<thead>
<tr>
<th>分类</th>
<th>描述</th>
</tr>
</thead>
<tbody><tr>
<td>1XX</td>
<td>指示信息–表示请求正在处理</td>
</tr>
<tr>
<td>2XX</td>
<td><strong>成功</strong>–表示请求已被成功处理完毕</td>
</tr>
<tr>
<td>3XX</td>
<td><strong>重定向</strong>–要完成的请求需要进行附加操作</td>
</tr>
<tr>
<td>4XX</td>
<td><strong>客户端错误</strong>–请求有语法错误或者请求无法实现，服务器无法处理请求</td>
</tr>
<tr>
<td>5XX</td>
<td><strong>服务器端错误</strong>–服务器处理请求出现错误</td>
</tr>
</tbody></table>
<p>下面是常用的状态码分类：</p>
<h3 id="1xx"><a href="#1xx" class="headerlink" title="1xx"></a>1xx</h3><p>1xx 类状态码属于提示信息，是协议处理中的⼀种中间状态，实际⽤到的⽐较少。</p>
<h3 id="2xx"><a href="#2xx" class="headerlink" title="2xx"></a>2xx</h3><p>2xx 类状态码表示服务器成功处理了客户端的请求，也是我们最愿意看到的状态。</p>
<p>「<strong>200 OK</strong>」是最常⻅的成功状态码，表示⼀切正常。如果是⾮ HEAD 请求，服务器返回的响应头都会有 body</p>
<p>数据。</p>
<p>「<strong>204 No Content</strong>」也是常⻅的成功状态码，与 200 OK 基本相同，但响应头没有 body 数据。</p>
<p>「<strong>206 Partial Content</strong>」是应⽤于 HTTP 分块下载或断点续传，表示响应返回的 body 数据并不是资源的全部，⽽是其中的⼀部分，也是服务器处理成功的状态。</p>
<h3 id="3xx"><a href="#3xx" class="headerlink" title="3xx"></a>3xx</h3><p>3xx 类状态码表示客户端请求的资源发送了变动，需要客户端⽤新的 URL 重新发送请求获取资源，也就是重定</p>
<p>向。</p>
<p>「<strong>301 Moved Permanently</strong>」表示永久重定向，说明请求的资源已经不存在了，需改⽤新的 URL 再次访问。</p>
<p>「<strong>302 Found</strong>」表示临时重定向，说明请求的资源还在，但暂时需要⽤另⼀个 URL 来访问。</p>
<p>301 和 302 都会在响应头⾥使⽤字段 Location ，指明后续要跳转的 URL，浏览器会⾃动重定向新的 URL。 </p>
<p>「<strong>304 Not Modified</strong>」不具有跳转的含义，表示资源未修改，重定向已存在的缓冲⽂件，也称缓存重定向，⽤于缓存控制。</p>
<blockquote>
<p>重定向有两种：一种是302响应，称为临时重定向，一种是301响应，称为永久重定向。两者的区别是，如果服务器发送301永久重定向响应，浏览器会缓存<code>/hi</code>到<code>/hello</code>这个重定向的关联，下次请求<code>/hi</code>的时候，浏览器就直接发送<code>/hello</code>请求了。</p>
</blockquote>
<h3 id="4xx"><a href="#4xx" class="headerlink" title="4xx"></a>4xx</h3><p>4xx 类状态码表示客户端发送的报⽂有误，服务器⽆法处理，也就是错误码的含义。</p>
<p>「<strong>400 Bad Request</strong>」表示客户端请求的报⽂有错误，但只是个笼统的错误。</p>
<p>「<strong>403 Forbidden</strong>」表示服务器禁⽌访问资源，并不是客户端的请求出错。</p>
<p>「<strong>404 Not Found</strong>」表示请求的资源在服务器上不存在或未找到，所以⽆法提供给客户端。</p>
<h3 id="5xx"><a href="#5xx" class="headerlink" title="5xx"></a>5xx</h3><p>5xx 类状态码表示客户端请求报⽂正确，但是服务器处理时内部发⽣了错误，属于服务器端的错误码。</p>
<p>「<strong>500 Internal Server Error</strong>」与 400 类型，是个笼统通⽤的错误码，服务器发⽣了什么错误，我们并不知道。</p>
<p>「<strong>501 Not Implemented</strong>」表示客户端请求的功能还不⽀持，类似“即将开业，敬请期待”的意思。</p>
<p>「<strong>502 Bad Gateway</strong>」通常是服务器作为⽹关或代理时返回的错误码，表示服务器⾃身⼯作正常，访问后端服务器</p>
<p>发⽣了错误。</p>
<p>「<strong>503 Service Unavailable</strong>」表示服务器当前很忙，暂时⽆法响应服务器，类似“⽹络服务正忙，请稍后᯿试”的意思。</p>
<h2 id="HTTP方法"><a href="#HTTP方法" class="headerlink" title="HTTP方法"></a>HTTP方法</h2><p>HTTP&#x2F;0.9 只定义了一种方法: GET。</p>
<p>HTTP&#x2F;1.0 定义了三种请求方法：GET, POST 和 HEAD 方法。</p>
<p>HTTP&#x2F;1.1 增加了六种请求方法：OPTIONS, PUT, PATCH, DELETE, TRACE 和 CONNECT 方法。</p>
<table>
<thead>
<tr>
<th>方法</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>GET</td>
<td>请求指定的页面信息，并返回具体内容，通常只用于读取数据。</td>
</tr>
<tr>
<td>HEAD</td>
<td>类似于 GET 请求，只不过返回的响应中没有具体的内容，用于获取报头。</td>
</tr>
<tr>
<td>POST</td>
<td>向指定资源提交数据进行处理请求（例如提交表单或者上传文件）。数据被包含在请求体中。POST 请求可能会导致新的资源的建立或已有资源的更改。</td>
</tr>
<tr>
<td>PUT</td>
<td>替换指定的资源，没有的话就新增。</td>
</tr>
<tr>
<td>DELETE</td>
<td>请求服务器删除 URL 标识的资源数据。</td>
</tr>
<tr>
<td>CONNECT</td>
<td>将服务器作为代理，让服务器代替用户进行访问。</td>
</tr>
<tr>
<td>OPTIONS</td>
<td>向服务器发送该方法，会返回对指定资源所支持的 HTTP 请求方法。</td>
</tr>
<tr>
<td>TRACE</td>
<td>回显服务器收到的请求数据，即服务器返回自己收到的数据，主要用于测试和诊断。</td>
</tr>
<tr>
<td>PATCH</td>
<td>是对 PUT 方法的补充，用来对已知资源进行局部更新。</td>
</tr>
</tbody>
</table>



<p>用的最多的自然是<code>GET</code>、<code>POST</code>了， 甚至有一种观点认为只需要使用POST就够了。</p>
<h3 id="GET-和-POST-的区别"><a href="#GET-和-POST-的区别" class="headerlink" title="GET 和 POST 的区别"></a>GET 和 POST 的区别</h3><ul>
<li>get 提交的数据会放在 URL 之后，并且<strong>请求参数会被完整的保留在浏览器的记录</strong>里，由于<strong>参数直接暴露在 URL 中</strong>，可能会存在安全问题，因此往往用于获取资源信息。GET上要在url之外带一些参数就只能依靠url上附带querystring。</li>
<li>编码格式： get 请求只支持 URL 编码，post 请求支持多种编码格式。</li>
<li>字符集：get 只支持 ASCII 字符格式的参数，而 post 方法没有限制。</li>
<li>数据大小：get 提交的数据大小有限制（这里所说的<strong>限制是浏览器带来的</strong>，一般都限制在几千），而 post 方法提交的数据没限制</li>
<li>get 方式需要使用 queryString 来取得变量的值，而 post 方式通过将body解析成实体类来获取（当然还有其他的方式）。</li>
<li>TCP数据包：get 方法产生一个 TCP 数据包，post 方法产生两个（<strong>并不是所有的浏览器中都产生两个</strong>）。</li>
</ul>
<h2 id="特点（优点和缺点）"><a href="#特点（优点和缺点）" class="headerlink" title="特点（优点和缺点）"></a>特点（优点和缺点）</h2><h2 id="HTTP-如何保存状态"><a href="#HTTP-如何保存状态" class="headerlink" title="HTTP 如何保存状态"></a>HTTP 如何保存状态</h2><p>因为一个 HTTP 服务器并不保存关于客户机的任何信息，所以我们说 HTTP 是一个无状态协议。</p>
<p>通常有两种解决方案：</p>
<p>① <strong>基于 Session 实现的会话保持</strong></p>
<p>在客户端第一次向服务器发送 HTTP 请求后，<strong>服务器会创建一个 Session 对象并将客户端的身份信息以键值对的形式存储下来</strong>，然后<strong>分配一个会话标识（SessionId）给客户端</strong>，这个<strong>会话标识一般保存在客户端 Cookie 中</strong>，之后每次该浏览器<strong>发送 HTTP 请求都会带上 Cookie 中的 SessionId 到服务器</strong>，服务器根据会话标识就可以将之前的状态信息与会话联系起来，从而实现会话保持。</p>
<ul>
<li><p>优点：<strong>安全性高</strong>，因为状态信息保存在服务器端。</p>
</li>
<li><p><strong>缺点</strong>：**水平拓展差，由于大型网站往往采用的是分布式服务器，浏览器发送的 HTTP 请求一般要先通过负载均衡器才能到达具体的后台服务器，倘若同一个浏览器两次 HTTP 请求分别落在不同的服务器上时，基于 Session 的方法就不能实现会话保持了。</p>
</li>
</ul>
<p>【解决方法：<strong>采用中间件</strong>，例如 Redis，我们通过<strong>将 Session 的信息存储在 Redis 中</strong>，使得每个服务器都可以访问到之前的状态信息】</p>
<p>② <strong>基于 Cookie 实现的会话保持</strong></p>
<p>当服务器发送响应消息时，在 HTTP 响应头中设置 Set-Cookie 字段，用来存储客户端的状态信息。客户端解析出 HTTP 响应头中的字段信息，并根据其生命周期创建不同的 Cookie，这样一来每次浏览器发送 HTTP 请求的时候都会带上 Cookie 字段，从而实现状态保持。</p>
<p><strong>基于 Cookie 的会话保持与基于 Session 实现的会话保持最主要的区别是前者完全将会话状态信息存储在浏览器 Cookie 中。</strong></p>
<p><strong>优点</strong>：<strong>服务器无状态</strong>， 减轻服务器存储压力，同时便于服务端做水平拓展。</p>
<p><strong>缺点</strong>：该<strong>方式不够安全</strong>，因为状态信息存储在客户端，这意味着不能在会话中保存机密数据。除此之外，浏览器每次发起 HTTP 请求时都需要发送额外的 Cookie 到服务器端，会<strong>占用更多带宽</strong>。</p>
<p><strong>拓展：Cookie被禁用了怎么办？</strong></p>
<p>若遇到 Cookie 被禁用的情况，则可以通过重写 URL 的方式将会话标识放在 URL 的参数里，也可以实现会话保持。</p>
<blockquote>
<p><strong>URL重写</strong>（英语：URL Rewriting）是一种REST的相关技术，它可以在Web Server中，针对用户所提供的URL进行转换后，再传入Web Server中的程序处理器。</p>
<p>如果浏览器不支持Cookie或用户阻止了所有Cookie，可以把会话ID附加在HTML页面中所有的URL上，这些页面作为响应发送给客户。这样，当用户单击URL时，会话ID被自动作为请求行的一部分而不是作为头行发送回服务器。这种方法称为URL重写(URL rewriting)。</p>
</blockquote>
<p>详细参考<a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/URL%E9%87%8D%E5%AF%AB">URL重写</a></p>
<h1 id="HTTP-与-HTTPs"><a href="#HTTP-与-HTTPs" class="headerlink" title="HTTP 与 HTTPs"></a>HTTP 与 HTTPs</h1><p>HTTPS 采用对称加密和非对称加密相结合的方式，首先使用 SSL&#x2F;TLS 协议进行加密传输，为了弥补非对称加密的缺点，HTTPS <strong>①采用证书来进一步加强非对称加密的安全性</strong>，<strong>②使用非对称加密交换后面通信传输用到的<em>会话密钥</em></strong>，<strong>③后续的所有信息都通过使用该对称秘钥进行加密解密</strong>，完成整个 HTTPS 的流程。</p>
<h2 id="HTTP-与-HTTPS的区别"><a href="#HTTP-与-HTTPS的区别" class="headerlink" title="HTTP 与 HTTPS的区别"></a>HTTP 与 HTTPS的区别</h2><ol>
<li><strong>HTTP 是超⽂本传输协议，信息是明⽂传输</strong>，存在安全⻛险的问题。HTTPS 则解决 HTTP 不安全的缺陷，在TCP 和 HTTP ⽹络层之间加⼊了<em>SSL&#x2F;TLS</em>安全协议，使得<strong>报⽂能够加密传输</strong>。</li>
<li>HTTP 连接建⽴相对简单， TCP 三次握⼿之后便可进⾏ HTTP 的报⽂传输。⽽ <strong>HTTPS 在 TCP 三次握⼿之后，还需进⾏ SSL&#x2F;TLS 的握⼿过程</strong>，才可进⼊加密报⽂传输。</li>
<li>HTTP 的端⼝号是 80，HTTPS 的端⼝号是 443。</li>
<li>HTTPS 协议需要<strong>向 CA（证书权威机构）申请数字证书</strong>，来保证服务器的身份是可信的。</li>
</ol>
<blockquote>
<p>SSL 是 “<em>Secure Sockets Layer</em> 的缩写，中⽂叫做「安全套接层」。它是在上世纪 90 年代中期，由⽹景公司</p>
<p>设计的。</p>
<p>到了1999年，SSL 因为应⽤⼴泛，已经成为互联⽹上的事实标准。IETF 就在那年把 SSL 标准化。标准化之后的名</p>
<p>称改为 TLS（是 “<em>Transport Layer Security</em>” 的缩写），中⽂叫做 「传输层安全协议」。</p>
<p>很多相关的⽂章都把这两者并列称呼（SSL&#x2F;TLS），因为这两者可以视作同⼀个东⻄的不同阶段。</p>
</blockquote>
<h2 id="HTTPS-解决了-HTTP-的哪些问题"><a href="#HTTPS-解决了-HTTP-的哪些问题" class="headerlink" title="HTTPS 解决了 HTTP 的哪些问题"></a>HTTPS 解决了 HTTP 的哪些问题</h2><p>HTTP 由于是明⽂传输，所以安全上存在以下三个⻛险：</p>
<ol>
<li><p>窃听⻛险，⽐如通信链路上可以获取通信内容；</p>
</li>
<li><p>篡改⻛险，⽐如强制植⼊垃圾⼴告；</p>
</li>
<li><p>冒充⻛险，⽐如冒充淘宝⽹站。</p>
</li>
</ol>
<img src="https1.png" style="zoom:80%;" />





<p>HTTP<strong>S</strong> 在 HTTP 与 TCP 层之间加⼊了 SSL&#x2F;TLS 协议，可以很好的解决了上述的⻛险：</p>
<p>信息加密：交互信息⽆法被窃取。</p>
<p>校验机制：⽆法篡改通信内容，篡改了就不能正常显示。</p>
<p>身份证书：证明淘宝是真的淘宝⽹。</p>
<blockquote>
<p>HTTPS 是如何解决上⾯的三个⻛险的？</p>
</blockquote>
<ul>
<li><p><strong>混合加密</strong>的⽅式实现信息的<strong>机密性</strong>，解决了窃听的⻛险。</p>
</li>
<li><p><strong>摘要算法</strong>的⽅式来实现<strong>完整性</strong>，它能够为数据⽣成独⼀⽆⼆的<em>指纹</em>，指纹⽤于校验数据的完整性，解决</p>
</li>
</ul>
<p>了篡改的⻛险。</p>
<ul>
<li>将服务器公钥放⼊到<strong>数字证书</strong>中，解决了<strong>冒充⻛险</strong>。</li>
</ul>
<blockquote>
<p>这部分内容可以参考 <a target="_blank" rel="noopener" href="http://www.ruanyifeng.com/blog/2011/08/what_is_a_digital_signature.html">数字签名是什么？</a></p>
</blockquote>
<h3 id="1-混合加密"><a href="#1-混合加密" class="headerlink" title="1. 混合加密"></a>1. 混合加密</h3><img src="秘钥.png" style="zoom:80%;" />





<p>HTTPS 采⽤的是对称加密和⾮对称加密结合的<strong>混合加密</strong>⽅式：</p>
<ul>
<li><p>在通信建⽴前采⽤⾮对称加密的⽅式交换<strong>会话秘钥</strong>，后续就不再使⽤⾮对称加密。</p>
</li>
<li><p>在通信过程中全部使⽤对称加密的<strong>会话秘钥</strong>加密明⽂数据。</p>
</li>
</ul>
<p>采⽤「混合加密」的⽅式的原因：</p>
<ul>
<li><p><strong>对称加密只使⽤⼀个密钥，运算速度快</strong>，密钥必须保密，<strong>但⽆法做到安全的密钥交换</strong>。</p>
</li>
<li><p><strong>⾮对称加密使⽤两个密钥</strong>：公钥和私钥，公钥可以任意分发⽽私钥保密，<strong>解决了密钥交换问题但速度慢</strong>。</p>
</li>
</ul>
<h3 id="2-摘要算法"><a href="#2-摘要算法" class="headerlink" title="2. 摘要算法"></a>2. 摘要算法</h3><p><strong>摘要算法</strong>⽤来实现完整性，能够为数据⽣成独⼀⽆⼆的<em>指纹</em>，<strong>⽤于校验数据的完整性</strong>，解决了篡改的⻛险。</p>
<img src="摘要.png" style="zoom:80%;" />



<p>客户端在发送明⽂之前会通过摘要算法算出明⽂的「指纹」，发送的时候把「指纹 + 明⽂」⼀同加密成密⽂后，发</p>
<p>送给服务器，服务器解密后，⽤相同的摘要算法算出发送过来的明⽂，通过⽐较客户端携带的「指纹」和当前算出</p>
<p>的「指纹」做⽐较，若「指纹」相同，说明数据是完整的。</p>
<h3 id="3-数字证书"><a href="#3-数字证书" class="headerlink" title="3. 数字证书"></a>3. 数字证书</h3><p>客户端先向服务器端索要公钥，然后⽤公钥加密信息，服务器收到密⽂后，⽤⾃⼰的私钥解密。</p>
<p>这就存在些问题，如何保证公钥不被篡改和信任度？</p>
<p>所以这⾥就需要借助第三⽅权威机构 CA （certificate authority  数字证书认证机构），<strong>将服务器公钥放在数字证书</strong>（由数字证书认证机构颁发）中，只要证书是可信的，公钥就是可信的。</p>
<img src="数字证书.png" style="zoom:70%;" />



<p>通过数字证书的⽅式保证服务器公钥的身份，解决冒充的⻛险。</p>
<h3 id="名词总结"><a href="#名词总结" class="headerlink" title="名词总结"></a>名词总结</h3><ol>
<li>秘钥：某个用来完成加密、解密、完整性验证等密码学应用的秘密信息。在对称加密中加密解密使用的是同一个秘钥；在非对称加密中，又分为公钥和私钥。</li>
<li>摘要：根据一定的运算规则对原数据进行某种形式的提取，这种提取就是摘要。可以用于数据完整性验证。由于摘要算法不可逆，理论上无法通过反向运算取得原数据内容。摘要算法有MD5等。</li>
<li>数字签名：一种功能类似写在纸上的普通签名、但是使用了<em>公钥加密</em> 领域的技术，以用于鉴别数字信息的方法。数字签名算法是依靠<em>公钥加密技术</em>来实现的。RSA等算法可以实现数字签名。</li>
<li>数字证书：由证书中心（Certificate Authority）颁发的用于标志通讯各方身份信息的一个数字认证。在SSL中，①服务器将自己的公钥和自身的其他信息向CA注册，②CA再用自己的私钥加密成数字证书颁发给，③然后客户端再用CA的公钥去解密数字证书，如果合法就可以得到可靠的服务器的公钥。</li>
</ol>
<h3 id="客户端为什么信任第三方证书"><a href="#客户端为什么信任第三方证书" class="headerlink" title="客户端为什么信任第三方证书"></a>客户端为什么信任第三方证书</h3><ol>
<li><p>没有CA的私钥无法篡改签名<br><strong>假设中间人篡改了证书原文，由于他没有 CA 机构的私钥，所以无法得到此时加密后的签名，因此无法篡改签名。</strong>客户端浏览器收到该证书后会发现原文和签名解密后的值不一致，则说明证书被中间人篡改，证书不可信，从而终止向服务器传输信息。</p>
</li>
<li><p>替换证书但是域名不一致<br>上述过程说明证书无法被篡改，我们考虑更严重的情况，例如中间人拿到了 CA 机构认证的证书，它想窃取网站 A 发送给客户端的信息，于是它成为中间人拦截到了 A 传给客户端的证书，然后将其替换为自己的证书。此时客户端浏览器收到的是被中间人掉包后的证书，但由于证书里包含了客户端请求的网站信息，因此客户端浏览器只需要把证书里的域名与自己请求的域名比对一下就知道证书有没有被掉包了。</p>
</li>
</ol>
<p>CA之所以是CA，就是因为它是普遍被大家接受的权威结构，里面罗列着一些普遍被大家认可的证书，而这些通常会内置在浏览器中。</p>
<p>如果CA不被信任或者CA带头作恶，那相当于法官带头犯罪，什么手段都规避不了。</p>
<h2 id="HTTP-与-HTTPs-的工作方式（建立连接的过程）"><a href="#HTTP-与-HTTPs-的工作方式（建立连接的过程）" class="headerlink" title="HTTP 与 HTTPs 的工作方式（建立连接的过程）"></a>HTTP 与 HTTPs 的工作方式（建立连接的过程）</h2><h3 id="HTTP-1"><a href="#HTTP-1" class="headerlink" title="HTTP"></a>HTTP</h3><p>HTTP 使用 TCP（而不是 UDP）作为它的支撑运输层协议。其默认工作在 TCP 协议 80 端口，HTTP 客户机发起一个与服务器的 TCP 连接，一旦连接建立，浏览器和服务器进程就可以通过套接字接口访问 TCP。客户机从套接字接口发送 HTTP 请求报文和接收 HTTP 响应报文。类似地，服务器也是从套接字接口接收 HTTP 请求报文和发送 HTTP 响应报文。其通信内容以明文的方式发送，不通过任何方式的数据加密。当通信结束时，客户端与服务器关闭连接。</p>
<h3 id="HTTPS"><a href="#HTTPS" class="headerlink" title="HTTPS"></a>HTTPS</h3><p>HTTPS 协议基本流程：</p>
<ol>
<li><p>客户端<strong>向服务器索要并验证服务器的公钥</strong>。</p>
</li>
<li><p><strong>双⽅协商⽣产「会话秘钥」</strong>。</p>
</li>
<li><p><strong>双⽅采⽤「会话秘钥」进⾏加密通信</strong>。</p>
</li>
</ol>
<h4 id="SSL-x2F-TLS-建立过程"><a href="#SSL-x2F-TLS-建立过程" class="headerlink" title="SSL&#x2F;TLS 建立过程"></a>SSL&#x2F;TLS 建立过程</h4><p>前两步也就是 SSL&#x2F;TLS 的建⽴过程，也就是握⼿阶段。</p>
<p>SSL&#x2F;TLS 的「握⼿阶段」涉及四次通信，可⻅下图：</p>
<p><img src="/2021/11/26/HTTP/SSL.png"></p>
<p>SSL&#x2F;TLS 协议建⽴的详细流程：</p>
<ol>
<li><strong>ClientHello</strong></li>
</ol>
<p>⾸先，由客户端向服务器发起加密通信请求，也就是 ClientHello 请求。</p>
<p>在这⼀步，客户端主要向服务器发送以下信息：</p>
<p>（1）客户端⽀持的 SSL&#x2F;TLS 协议版本，如 TLS 1.2 版本。</p>
<p>（2）客户端⽣产的随机数（ <strong>Client Random</strong> ），后⾯⽤于⽣产「会话秘钥」。</p>
<p>（3）客户端⽀持的密码套件列表，如 RSA 加密算法。</p>
<ol start="2">
<li><strong>SeverHello</strong></li>
</ol>
<p>服务器收到客户端请求后，向客户端发出响应，也就是 SeverHello 。服务器回应的内容有如下内容：</p>
<p>（1）确认 SSL&#x2F; TLS 协议版本，如果浏览器不⽀持，则关闭加密通信。</p>
<p>（2）服务器⽣产的随机数（ <strong>Server Random</strong> ），后⾯⽤于⽣产「会话秘钥」。</p>
<p>（3）确认的密码套件列表，如 RSA 加密算法。</p>
<p>（4）<strong>服务器的数字证书</strong>。</p>
<ol start="3">
<li><strong>客户端回应</strong></li>
</ol>
<p>客户端收到服务器的回应之后，<strong>⾸先通过浏览器或者操作系统中的 CA 公钥，确认服务器的数字证书的真实性</strong>。</p>
<p>如果证书没有问题，客户端会<strong>从数字证书中取出服务器的公钥</strong>，然后<strong>使⽤它加密报⽂</strong>，向服务器发送如下信息：</p>
<p>（1）<strong>⼀个随机数（ pre-master key ）</strong>。该随机数会被服务器公钥加密。</p>
<p>（2）<strong>加密通信算法改变通知，表示随后的信息都将⽤「会话秘钥」加密通信。</strong></p>
<p>（3）客户端握⼿结束通知，表示客户端的握⼿阶段已经结束。这⼀项同时<strong>把之前所有内容的发⽣的数据做个摘</strong></p>
<p><strong>要，⽤来供服务端校验</strong>。</p>
<p>上⾯第⼀项的随机数是整个握⼿阶段的第三个随机数，这样<strong>服务器和客户端就同时有三个随机数</strong>，接着就⽤<strong>双⽅协商的加密算法，各⾃⽣成本次通信的「会话秘钥」</strong>。</p>
<ol start="4">
<li><strong>服务器的最后回应</strong></li>
</ol>
<p>服务器收到客户端的<strong>第三个随机数（ pre-master key ）</strong>之后，通过协商的加密算法，计算出<strong>本次通信的「会话秘钥」</strong>。然后，向客户端发⽣最后的信息：</p>
<p>（1）<strong>加密通信算法改变通知，表示随后的信息都将⽤「会话秘钥」加密通信。</strong></p>
<p>（2）服务器握⼿结束通知，表示服务器的握⼿阶段已经结束。这⼀项同时<strong>把之前所有内容的发⽣的数据做个摘</strong></p>
<p><strong>要，⽤来供客户端校验</strong>。</p>
<p>⾄此，整个 SSL&#x2F;TLS 的握⼿阶段全部结束。接下来，客户端与服务器进⼊加密通信，就完全是使⽤普通的 HTTP</p>
<p>协议，只不过⽤「会话秘钥」加密内容。</p>
<blockquote>
<p>简洁版</p>
<p>TLS握手协议 </p>
<ol>
<li>客户端发出一个 client hello 消息，携带的信息包括：所支持的 SSL&#x2F;TLS 版本列表；支持的与加密算法；所支持的数据压缩方法；随机数 A。</li>
<li>服务端响应一个 server hello 消息，携带的信息包括：协商采用的 SSL&#x2F;TLS 版本号；会话 ID；随机数 B；服务端数字证书 serverCA；由于双向认证需求，服务端需要对客户端进行认证，会同时发送一个 client certificate request，表示请求客户端的证书。</li>
<li>客户端校验服务端的数字证书；校验通过之后发送随机数 C，该随机数称为 pre-master-key，使用数字证书中的公钥加密后发出；由于服务端发起了 client certificate request，客户端使用私钥加密一个随机数 clientRandom 随客户端的证书 clientCA 一并发出。</li>
<li>服务端校验客户端的证书，并成功将客户端加密的随机数 clientRandom 解密；根据随机数 A&#x2F;随机数 B&#x2F;随机数 C（pre-master-key） 产生动态密钥 master-key，加密一个 finish 消息发至客户端。</li>
<li>客户端根据同样的随机数和算法生成 master-key，加密一个 finish 消息发送至服务端。</li>
<li>服务端和客户端分别解密成功，至此握手完成，之后的数据包均采用 master-key 进行加密传输。</li>
</ol>
</blockquote>
<h1 id="HTTP-x2F-1-1、HTTP-x2F-2、HTTP-x2F-3-演变"><a href="#HTTP-x2F-1-1、HTTP-x2F-2、HTTP-x2F-3-演变" class="headerlink" title="HTTP&#x2F;1.1、HTTP&#x2F;2、HTTP&#x2F;3 演变"></a>HTTP&#x2F;1.1、HTTP&#x2F;2、HTTP&#x2F;3 演变</h1><h2 id="HTTP-x2F-1-1-和-HTTP-x2F-1-0-的区别"><a href="#HTTP-x2F-1-1-和-HTTP-x2F-1-0-的区别" class="headerlink" title="HTTP&#x2F;1.1 和 HTTP&#x2F;1.0 的区别"></a>HTTP&#x2F;1.1 和 HTTP&#x2F;1.0 的区别</h2><ol>
<li><p><strong>缓存处理</strong>：在 HTTP&#x2F;1.0 中主要使用 header 里的 if-modified-Since, Expries 来做缓存判断的标准。而 HTTP&#x2F;1.1 请求头中添加了更多与缓存相关的字段，从而支持更为灵活的缓存策略，例如 <code>Entity-tag</code>,<code> If-Unmodified-Since</code>, <code>If-Match</code>,<code> If-None-Match</code> 等可供选择的缓存头来控制缓存策略。</p>
</li>
<li><p><strong>分片传输</strong>： 当客户端请求某个资源时，HTTP&#x2F;1.0 默认将该资源相关的整个对象传送给请求方，但很多时候可能客户端并不需要对象的所有信息。而在 <strong>HTTP&#x2F;1.1 的请求头中引入了 range 头域</strong>，它允许只请求部分资源，其使得开发者可以多线程请求某一资源，从而充分的利用带宽资源，实现高效并发。</p>
</li>
<li><p><strong>长连接</strong>：HTTP&#x2F;1.0 默<strong>认浏览器和服务器之间保持短暂连接</strong>，<strong>浏览器的每次请求都需要与服务器建立一个 TCP 连接，服务器完成后立即断开 TCP 连接</strong>。HTTP&#x2F;1.1 默认使用的是持久连接，其支持在同一个 TCP 请求中传送多个 HTTP 请求和响应。此之前的 HTTP 版本的默认连接都是使用非持久连接，如果想要在旧版本的 HTTP 协议上维持持久连接，则需要指定 Connection 的首部字段的值为 Keep-Alive。</p>
</li>
<li><p><strong>管道（pipeline）⽹络传输</strong>，只要第⼀个请求发出去了，不必等其回来，就可以发第⼆个请求出去，可以减少整体的响应时间。</p>
</li>
<li><p>错误通知的管理：HTTP&#x2F;1.1 在 1.0 的基础上新增了 24 个错误状态响应码，例如 414 表示客户端请求中所包含的 URL 地址太长，以至于服务器无法处理；410 表示所请求的资源已经被永久删除。</p>
</li>
<li><p>Host 请求头：早期 HTTP&#x2F;1.0 中认为每台服务器都绑定一个唯一的 IP 地址并提供单一的服务，请求消息中的 URL 并没有传递主机名。而随着虚拟主机的出现，<strong>一台物理服务器上可以存在多个虚拟主机，并且它们共享同一个 IP 地址</strong>。为了支持虚拟主机，HTTP&#x2F;1.1 中添加了 host 请求头，请求消息和响应消息中应声明这个字段，若请求消息中缺少该字段时服务端会响应一个 404 错误状态码。</p>
</li>
</ol>
<h2 id="HTTP-x2F-1-X-和-HTTP-x2F-2-0-的区别"><a href="#HTTP-x2F-1-X-和-HTTP-x2F-2-0-的区别" class="headerlink" title="HTTP&#x2F;1.X 和 HTTP&#x2F;2.0 的区别"></a>HTTP&#x2F;1.X 和 HTTP&#x2F;2.0 的区别</h2><p>但 HTTP&#x2F;1.1 还是有性能瓶颈 : </p>
<ul>
<li><p>请求 &#x2F; 响应头部（Header）未经压缩就发送，⾸部信息越多延迟越⼤。只能压缩 Body 的部分；</p>
</li>
<li><p>发送冗⻓的⾸部。每次互相发送相同的⾸部造成的浪费较多；</p>
</li>
<li><p>服务器是按请求的顺序响应的，如果服务器响应慢，会招致客户端⼀直请求不到数据，也就是<strong>队头阻塞；</strong></p>
</li>
<li><p>请求只能从客户端开始，<strong>服务器只能被动响应</strong>。</p>
</li>
</ul>
<p>HTTP&#x2F;2 协议是基于 HTTPS 的，所以 HTTP&#x2F;2 的安全性也是有保障的。</p>
<h3 id="1-头部压缩"><a href="#1-头部压缩" class="headerlink" title="1. 头部压缩"></a>1. 头部压缩</h3><p>HTTP&#x2F;2 会压缩头（Header）<strong>如果你同时发出多个请求，他们的头是⼀样的或是相似的，那么，协议会帮你消除重复的部分</strong>。<br>这就是所谓的 HPACK 算法：<strong>在客户端和服务器同时维护⼀张头信息表，所有字段都会存⼊这个表，⽣成⼀个索引号，以后就不发送同样字段了，只发送索引号</strong>，这样就提⾼速度了。</p>
<h3 id="2-⼆进制格式"><a href="#2-⼆进制格式" class="headerlink" title="2. ⼆进制格式"></a>2. ⼆进制格式</h3><p>HTTP&#x2F;2 不再像 HTTP&#x2F;1.1 ⾥的纯⽂本形式的报⽂，⽽是全⾯采⽤了⼆进制格式，头信息和数据体都是⼆进制，并且统称为帧（frame）：头信息帧和数据帧。</p>
<p><img src="/2021/11/26/HTTP/%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%A0%BC%E5%BC%8F.png"></p>
<p>这样虽然对⼈不友好，但是对计算机⾮常友好，因为计算机只懂⼆进制，那么收到报⽂后，⽆需再将明⽂的报⽂转<br>成⼆进制，⽽是直接解析⼆进制报⽂，这<strong>增加了数据传输的效率</strong>。</p>
<h3 id="3-数据流"><a href="#3-数据流" class="headerlink" title="3. 数据流"></a>3. 数据流</h3><p>HTTP&#x2F;2 的数据包不是按顺序发送的，同⼀个连接⾥⾯连续的数据包，可能属于不同的回应。因此，必须要对数据包做标记，指出它属于哪个回应。<br>每个请求或回应的所有数据包，称为⼀个数据流（ Stream ）。每个数据流都标记着⼀个独⼀⽆⼆的编号，其中规定客户端发出的数据流编号为奇数， 服务器发出的数据流编号为偶数</p>
<p>客户端还可以<strong>指定数据流的优先级</strong>。优先级⾼的请求，服务器就先响应该请求。</p>
<h3 id="4-多路复⽤"><a href="#4-多路复⽤" class="headerlink" title="4. 多路复⽤"></a>4. 多路复⽤</h3><p>HTTP&#x2F;2 是可以<strong>在⼀个连接中并发多个请求或回应，⽽不⽤按照顺序⼀⼀对应</strong>。</p>
<p><strong>移除了 HTTP&#x2F;1.1 中的串⾏请求</strong>，不需要排队等待，也就<strong>解决了「队头阻塞」问题（一定程度上），降低了延迟</strong>，⼤幅度提⾼</p>
<p>了连接的利⽤率。</p>
<p>举例来说，在⼀个 TCP 连接⾥，服务器收到了客户端 A 和 B 的两个请求，如果发现 A 处理过程⾮常耗时，于是就</p>
<p>回应 A 请求已经处理好的部分，接着回应 B 请求，完成后，再回应 A 请求剩下的部分。</p>
<p><img src="/2021/11/26/HTTP/%E5%A4%9A%E8%B7%AF%E5%A4%8D%E7%94%A8.png"></p>
<h3 id="5-服务器推送"><a href="#5-服务器推送" class="headerlink" title="5. 服务器推送"></a>5. 服务器推送</h3><p>HTTP&#x2F;2 还在⼀定程度上改善了传统的「请求 - 应答」⼯作模式，服务不再是被动地响应，也可以主动向客户端发</p>
<p>送消息。举例来说，在浏览器刚请求 HTML 的时候，就提前把可能会⽤到的 JS、CSS ⽂件等静态资源主动发给客户端，减少延时的等待，也就是服务器推送（Server Push，也叫 Cache Push）。</p>
<h2 id="HTTP-x2F-2-0-和-HTTP-x2F-3-0-的区别"><a href="#HTTP-x2F-2-0-和-HTTP-x2F-3-0-的区别" class="headerlink" title="HTTP&#x2F;2.0 和 HTTP&#x2F;3.0 的区别"></a>HTTP&#x2F;2.0 和 HTTP&#x2F;3.0 的区别</h2><p>HTTP&#x2F;2 主要的问题在于，多个 HTTP 请求在复⽤⼀个 TCP 连接，下层的 TCP 协议是不知道有多少个 HTTP 请求<br>的。所以⼀旦发⽣了丢包现象，就会触发 TCP 的重传机制，这样在⼀个 TCP 连接中的所有的 HTTP 请求都必须等待这个丢了的包被重传回来。</p>
<ul>
<li><strong>队头阻塞问题</strong>：HTTP&#x2F;1.1 中的管道（ pipeline）传输中如果有⼀个请求阻塞了，那么队列后面请求也统统被阻塞住了</li>
<li><strong>复用TCP的丢包问题</strong>：HTTP&#x2F;2 多个请求复⽤⼀个TCP连接，⼀旦发⽣丢包，就会阻塞住所有的 HTTP 请求。</li>
</ul>
<p>这都是基于 TCP 传输层的问题，所以 HTTP&#x2F;3 把 HTTP 下层的 TCP 协议改成了 UDP！</p>
<img src="http3.0.png" style="zoom:70%;" />





<blockquote>
<p>⼤家都知道 UDP 是不可靠传输的，但基于 UDP 的 <strong>QUIC</strong> 协议 可以实现类似 TCP 的可靠性传输。</p>
<p>QUIC 有⾃⼰的⼀套机制可以保证传输的可靠性的。当某个流发⽣丢包时，只会阻塞这个流，其他流不会受到</p>
<p>影响。</p>
<p>TLS3 升级成了最新的 1.3 版本，头部压缩算法也升级成了 QPack 。</p>
<p>HTTPS 要建⽴⼀个连接，要花费 6 次交互，先是建⽴三次握⼿，然后是 TLS&#x2F;1.3 的三次握⼿。QUIC 直接</p>
<p>把以往的 TCP 和 TLS&#x2F;1.3 的 6 次交互合并成了 <strong>3</strong> 次，减少了交互次数。</p>
<p>所以， <strong>QUIC 是⼀个在 UDP 之上的伪 TCP + TLS + HTTP&#x2F;2 的多路复⽤</strong>的协议。</p>
<p>QUIC 是新协议，对于很多⽹络设备，根本不知道什么是 QUIC，只会当做 UDP，这样会出现新的问题。所以</p>
<p>HTTP&#x2F;3 现在普及的进度⾮常的缓慢，不知道未来 UDP 是否能够逆袭 TCP。</p>
</blockquote>
<h2 id="了解HTTP3"><a href="#了解HTTP3" class="headerlink" title="了解HTTP3"></a>了解HTTP3</h2><h3 id="HTTP-x2F-2-存在的问题"><a href="#HTTP-x2F-2-存在的问题" class="headerlink" title="HTTP&#x2F;2 存在的问题"></a>HTTP&#x2F;2 存在的问题</h3><p>我们知道，传统 Web 平台的数据传输都基于 TCP 协议，而 TCP 协议在创建连接之前不可避免的需要三次握手，如果需要提高数据交互的安全性，即增加传输层安全协议（TLS），还会增加更多的握手次数。</p>
<p> HTTP 从 1.0 到 2.0，其传输层都是基于 TCP 协议的。即使是带来巨大性能提升的 HTTP&#x2F;2，也无法完全解决 TCP 协议存在的固有问题（慢启动，拥塞窗口尺寸的设置等）。</p>
<p><strong>此外，HTTP&#x2F;2 多路复用只是减少了连接数，其队头的拥塞问题并没有完全解决</strong>，倘若 TCP 丢包率过大，则 HTTP&#x2F;2 的表现将不如 HTTP&#x2F;1.1。</p>
<h3 id="QUIC-协议"><a href="#QUIC-协议" class="headerlink" title="QUIC 协议"></a>QUIC 协议</h3><p>QUIC（Quick UDP Internet Connections），直译为<strong>快速 UDP 网络连接</strong>，是谷歌制定的一种基于 UDP 的低延迟传输协议。其主要目的是解决采用传输层 TCP 协议存在的问题，同时满足传输层和应用层对多连接、低延迟等的需求。该协议融合了 TCP, TLS, HTTP&#x2F;2 等协议的特性，并基于 UDP传输。该协议带来的主要提升有：</p>
<p>低延迟连接。当客户端第一次连接服务器时，QUIC 只需要 1 RTT（Round-Trid Time）延迟就可以建立安全可靠的连接（采用 TLS 1.3 版本），相比于 TCP + TLS 的 3 次 RTT 要更加快捷。之后，客户端可以在本地缓存加密的认证信息，当再次与服务器建立连接时可以实现 0 RTT 的连接建立延迟。</p>
<p>QUIC 复用了 HTTP&#x2F;2 协议的多路复用功能，由于 QUIC 基于 UDP，所以也避免了 HTTP&#x2F;2存在的队头阻塞问题。</p>
<p>基于 UDP 协议的 QUIC 运行在用户域而不是系统内核，这使得 QUIC 协议可以快速的更新和部署，从而很好地解决了 TPC 协议部署及更新的困难。</p>
<p>QUIC 的报文是经过加密和认证的，除了少量的报文，其它所有的 QUIC 报文头部都经过了认证，报文主体经过了加密。只要有攻击者篡改 QUIC 报文，接收端都能及时发现。</p>
<p>具有向前纠错机制，每个数据包携带了除了本身内容外的部分其他数据包的内容，使得在出现少量丢包的情况下，尽量地减少其它包的重传次数，其通过牺牲单个包所携带的有效数据大小换来更少的重传次数，这在丢包数量较小的场景下能够带来一定程度的性能提升。</p>
<h3 id="HTTP-x2F-3"><a href="#HTTP-x2F-3" class="headerlink" title="HTTP&#x2F;3"></a>HTTP&#x2F;3</h3><p><strong>HTTP&#x2F;3 是在 QUIC 基础上发展起来的，其底层使用 UDP 进行数据传输</strong>，上层仍然使用 HTTP&#x2F;2。</p>
<p><strong>在 UDP 与 HTTP&#x2F;2 之间存在一个 QUIC 层</strong>，<strong>其中 TLS 加密过程在该层进行处理</strong>。</p>
<p>HTTP&#x2F;3 主要有以下几个特点：</p>
<p>① <strong>使用 UDP 作为传输层进行通信</strong>；</p>
<p>② <strong>在 UDP 之上的 QUIC 协议保证了 HTTP&#x2F;3 的安全性</strong>。QUIC 在建立连接的过程中就完成了 TLS 加密握手；</p>
<p>③ 建立连接快，正常只需要 1 RTT 即可建立连接。如果有缓存之前的会话信息，则直接验证和建立连接，此过程 0 RTT。建立连接时，也可以带有少量业务数据；</p>
<p>④ 不和具体底层连接绑定，QUIC 为每个连接的两端分别分配了一个唯一 ID，上层连接只认这对逻辑 ID。网络切换或者断连时，只需要继续发送数据包即可完成连接的建立；</p>
<p>⑤ 使用 QPACK 进行头部压缩，因为 在 HTTP&#x2F;2 中的 HPACK 要求传输过程有序，这会导致队头阻塞，而 QPACK 不存在这个问题。</p>
<blockquote>
<p><strong>tcp的慢</strong></p>
<ol>
<li>握手次数过多(TCP以及SSL&#x2F;TLS的握手)</li>
<li>防止拥塞的<strong>慢启动</strong></li>
<li><strong>丢包导致的队头阻塞</strong></li>
</ol>
<p><strong>quic为啥快</strong></p>
<ol>
<li><strong>减少往返次数RTT</strong>,缩短连接建立时间： 缓存对方的信息</li>
<li>连接多路复用，<strong>且采用独立数据流避免丢包阻塞</strong></li>
<li>使用前行纠错恢复丢失的包，减少超时重传</li>
<li>连接保存连接标志符，网络迁移连接快</li>
</ol>
</blockquote>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>如下为HTTP协议族的网络协议层架构：</p>
<img src="http123.png" style="zoom:80%;" />







<p>最后我们使用一张图来清晰的表示出 HTTP 协议的发展变化：</p>
<img src="http-all.png" style="zoom:48%;" />






<h1 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h1><p><a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/%E8%B6%85%E6%96%87%E6%9C%AC%E4%BC%A0%E8%BE%93%E5%8D%8F%E8%AE%AE">超文本传输协议 - 维基百科，自由的百科全书</a></p>
<p><a target="_blank" rel="noopener" href="https://www.ruanyifeng.com/blog/2016/08/http.html">HTTP 协议入门</a></p>
<p><a target="_blank" rel="noopener" href="http://www.ruanyifeng.com/blog/2011/08/what_is_a_digital_signature.html">数字签名是什么？</a></p>
<p><a target="_blank" rel="noopener" href="https://www.cnblogs.com/zhoudawei/p/9257276.html">nginx配置ssl证书实现https </a></p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/11/26/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E2%80%94%E2%80%94-%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F%E5%8F%8AJava%E5%AE%9E%E7%8E%B0/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/11/26/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E2%80%94%E2%80%94-%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F%E5%8F%8AJava%E5%AE%9E%E7%8E%B0/" class="post-title-link" itemprop="url">设计模式 —— 单例模式及Java实现</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2021-11-26 08:20:23 / 修改时间：08:21:59" itemprop="dateCreated datePublished" datetime="2021-11-26T08:20:23+08:00">2021-11-26</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/" itemprop="url" rel="index"><span itemprop="name">设计模式</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="单例模式"><a href="#单例模式" class="headerlink" title="单例模式"></a>单例模式</h1><p>在整个项目中只提供这个类的一个实例，而不是需要这个实例对象时候去创建，用完就销毁。</p>
<h2 id="适用场景"><a href="#适用场景" class="headerlink" title="适用场景"></a>适用场景</h2><p>创建此对象时间、空间开销非常大，而实际上这种类的对象具有静态属性，即属于类。<br>对于不同的调用者，不会因为需求不同，赋予它任意的性质——即这种类只会一种或少数形式创建出对象，完全可以事先创建创建、而后供人调用。<br>Spring的IOC容器里的对象的存在形式默认就是单例的。<br>常用于工具类，IO类、连接类的对象生成。</p>
<h1 id="Java实现"><a href="#Java实现" class="headerlink" title="Java实现"></a>Java实现</h1><h2 id="饿汉式"><a href="#饿汉式" class="headerlink" title="饿汉式"></a>饿汉式</h2><p>在编译时就加载完毕。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//饿汉式</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Singleton</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> <span class="type">Singleton</span> <span class="variable">singleton</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Singleton</span>();</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Singleton <span class="title function_">getSingleton</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> singleton;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="懒汉式"><a href="#懒汉式" class="headerlink" title="懒汉式"></a>懒汉式</h2><p>并不是程序初始时就生成对象，因为可能在整个程序运行时间都没有使用，那么懒汉式的加载就加大了开销。<br>也就是说，我们要<strong>在第一次要使用这个实例的时候才去创建它。</strong></p>
<ul>
<li>Java实现<br>这样就实现了懒加载的最基本的要求。<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//懒汉式</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Singleton</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">Singleton</span> <span class="variable">singleton</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Singleton <span class="title function_">getSingleton</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(singleton==<span class="literal">null</span>)&#123;</span><br><span class="line">            <span class="type">return</span> <span class="variable">singleton</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Singleton</span>();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> singleton;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
<h3 id="利用同步锁"><a href="#利用同步锁" class="headerlink" title="利用同步锁"></a>利用同步锁</h3><p>不难发现，上面的做法是线程不安全的。具体来说，有a、b、c三个线程调用了<code>getSingleton()</code>，它们在调用的时候，<code>singleton</code>都是<code>null</code>,于是三个线程都会创建出一个对象，破坏了单例的要求。<br>解决方法，也很简单。对这个方法加上同步锁，这样就保证同时只有一个线程抢到这把锁，然后第二个线程抢到这把锁的时候就不会创建了。<br>但是<code>synchronized</code>是重量级的同步锁，开销很大，这样使得每次调用这个方法都要进行同步操作，开销很大。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//懒汉式</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Singleton</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">Singleton</span> <span class="variable">singleton</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">static</span> Singleton <span class="title function_">getSingleton</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(singleton==<span class="literal">null</span>)&#123;</span><br><span class="line">            <span class="type">return</span> <span class="variable">singleton</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Singleton</span>();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> singleton;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>双重检查锁<br>下面的写法，并不是对方法加锁，而是在第一次判“空”的时候，才加锁，这样的话就不用每一次调用方法都会进行同步操作。<br>举个例子，有三个线程进入了if代码，然后它们将依次获得这把锁，而只有第一个抢到这把锁的线程才会去new一个实例，而之后的线程就会跳过if直接返回实例。<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//懒汉式</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Singleton</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">volatile</span> <span class="keyword">static</span> <span class="type">Singleton</span> <span class="variable">singleton</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Singleton <span class="title function_">getSingleton</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(singleton==<span class="literal">null</span>)&#123;</span><br><span class="line">            <span class="keyword">synchronized</span> (Singleton.class)&#123;</span><br><span class="line">                <span class="keyword">if</span>(singleton==<span class="literal">null</span>)&#123;</span><br><span class="line">                    singleton = <span class="keyword">new</span> <span class="title class_">Singleton</span>();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> singleton;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
但是，这样的代码还有可能犯的一个的错误。<br>要知道，在上述场景中有多个线程对同一个变量（共享变量）又读又写，那么为了保证每个线程都对<code>singleton</code>的值实时可见，那么必须对共享变量加上<code>volatile</code>关键字修饰，才能避免对数据脏读。</li>
</ul>
<h3 id="利用静态内部类"><a href="#利用静态内部类" class="headerlink" title="利用静态内部类"></a>利用静态内部类</h3><p>这种写法巧妙的利用了静态内部类只会在第一次使用时才加载的特性，实现了懒加载，并且是线程安全的。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//懒汉式</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Singleton</span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">SingletonHolder</span>&#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">Singleton</span> <span class="variable">singleton</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Singleton</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Singleton <span class="title function_">getSingleton</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> SingletonHolder.singleton;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>不过很可惜这种做法还是可以使用反射去破坏单例。<br>验证如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Singleton</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">SingletonHolder</span> &#123;</span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">static</span> <span class="type">Singleton</span> <span class="variable">singleton</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Singleton</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Singleton <span class="title function_">getSingleton</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> SingletonHolder.singleton;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException &#123;</span><br><span class="line">        Constructor&lt;Singleton&gt; constructor = Singleton.class.getConstructor();</span><br><span class="line">        <span class="type">Singleton</span> <span class="variable">singleton1</span> <span class="operator">=</span> constructor.newInstance();</span><br><span class="line">        <span class="type">Singleton</span> <span class="variable">singleton2</span> <span class="operator">=</span> constructor.newInstance();</span><br><span class="line">        <span class="type">Singleton</span> <span class="variable">singleton3</span> <span class="operator">=</span> constructor.newInstance();</span><br><span class="line">        System.out.println(singleton1);</span><br><span class="line">        System.out.println(singleton2);</span><br><span class="line">        System.out.println(singleton3);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


<h3 id="利用枚举实现单例"><a href="#利用枚举实现单例" class="headerlink" title="利用枚举实现单例"></a>利用枚举实现单例</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SingletonObject</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="title function_">SingletonObject</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (getSingletonObject() != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;new instance failure&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">enum</span> <span class="title class_">SingletonHolder</span> &#123;</span><br><span class="line">        INSTANCE;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> SingletonObject singletonObject;</span><br><span class="line"></span><br><span class="line">        SingletonHolder() &#123;</span><br><span class="line">            <span class="built_in">this</span>.singletonObject = <span class="keyword">new</span> <span class="title class_">SingletonObject</span>();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> SingletonObject <span class="title function_">getSingletonObject</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> SingletonHolder.INSTANCE.singletonObject;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="能否用“反射”去破坏单例"><a href="#能否用“反射”去破坏单例" class="headerlink" title="能否用“反射”去破坏单例"></a>能否用“反射”去破坏单例</h2><p>单例模式很重要的一点就是保证全局只有一份实例，那么我们能够通过反射去强行破坏这一要求，去生成多个实例呢？<br>很不幸的是，是可以的。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">    <span class="keyword">private</span> <span class="keyword">volatile</span> <span class="keyword">static</span> <span class="type">Singleton</span> <span class="variable">singleton</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Singleton <span class="title function_">getSingleton</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(singleton==<span class="literal">null</span>)&#123;</span><br><span class="line">            <span class="keyword">synchronized</span> (Singleton.class)&#123;</span><br><span class="line">                <span class="keyword">if</span>(singleton==<span class="literal">null</span>)&#123;</span><br><span class="line">                    singleton = <span class="keyword">new</span> <span class="title class_">Singleton</span>();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> singleton;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IllegalAccessException, InvocationTargetException, InstantiationException &#123;</span><br><span class="line">        <span class="type">Constructor</span> <span class="variable">constructor</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            constructor = Class.forName(<span class="string">&quot;单例模式.Singleton&quot;</span>).getConstructor();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (NoSuchMethodException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (ClassNotFoundException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        constructor.setAccessible(<span class="literal">true</span>);</span><br><span class="line">        <span class="type">Singleton</span> <span class="variable">singleton1</span> <span class="operator">=</span> (Singleton) constructor.newInstance();</span><br><span class="line">        <span class="type">Singleton</span> <span class="variable">singleton2</span> <span class="operator">=</span> (Singleton) constructor.newInstance();</span><br><span class="line">        <span class="type">Singleton</span> <span class="variable">singleton3</span> <span class="operator">=</span> (Singleton) constructor.newInstance();</span><br><span class="line">        System.out.println(singleton1);</span><br><span class="line">        System.out.println(singleton2);</span><br><span class="line">        System.out.println(singleton3);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">单例模式.Singleton@38af3868</span><br><span class="line">单例模式.Singleton@<span class="number">77459877</span></span><br><span class="line">单例模式.Singleton@5b2133b1</span><br></pre></td></tr></table></figure>
<p>不过反射因为是一种认为操作，这种风险往往是已知的。</p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/11/24/Kafka-1/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/11/24/Kafka-1/" class="post-title-link" itemprop="url">Kafka背景及架构介绍</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-11-24 17:25:57" itemprop="dateCreated datePublished" datetime="2021-11-24T17:25:57+08:00">2021-11-24</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-01-09 02:42:40" itemprop="dateModified" datetime="2023-01-09T02:42:40+08:00">2023-01-09</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E6%B6%88%E6%81%AF%E4%B8%AD%E9%97%B4%E4%BB%B6/" itemprop="url" rel="index"><span itemprop="name">消息中间件</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="Kafka简介"><a href="#Kafka简介" class="headerlink" title="Kafka简介"></a>Kafka简介</h1><p>Kafka是一种分布式的，基于发布&#x2F;订阅的消息系统。主要设计目标如下：</p>
<ul>
<li>快写：以时间复杂度为O(1)的方式提供消息持久化能力，即使对TB级以上数据也能保证常数时间复杂度的访问性能；</li>
<li>高吞吐率：即使在非常廉价的商用机器上也能做到单机支持每秒100K条以上消息的传输；</li>
<li>支持Kafka Server间的消息分区，及分布式消费，同时保证每个Partition内的消息顺序传输；</li>
<li>同时支持离线数据处理和实时数据处理；</li>
<li>扩容：支持在线水平扩展。</li>
</ul>
<h1 id="为何使用消息系统"><a href="#为何使用消息系统" class="headerlink" title="为何使用消息系统"></a>为何使用消息系统</h1><p>异步、削峰、解耦</p>
<ul>
<li><strong>解耦</strong><br>　　在项目启动之初来预测将来项目会碰到什么需求，是极其困难的。消息系统在处理过程中间插入了一个隐含的、基于数据的接口层，两边的处理过程都要实现这一接口。这允许你独立的扩展或修改两边的处理过程，只要确保它们遵守同样的接口约束。</li>
<li><strong>冗余</strong><br>　　有些情况下，处理数据的过程会失败。除非数据被持久化，否则将造成丢失。消息队列把数据进行持久化直到它们已经被完全处理，通过这一方式规避了数据丢失风险。许多消息队列所采用的”插入-获取-删除”范式中，在把一个消息从队列中删除之前，需要你的处理系统明确的指出该消息已经被处理完毕，从而确保你的数据被安全的保存直到你使用完毕。</li>
<li><strong>扩展性</strong><br>　　因为消息队列解耦了你的处理过程，所以增大消息入队和处理的频率是很容易的，只要另外增加处理过程即可。不需要改变代码、不需要调节参数。扩展就像调大电力按钮一样简单。</li>
<li><strong>灵活性 &amp; 峰值处理能力</strong><br>　　在访问量剧增的情况下，应用仍然需要继续发挥作用，但是这样的突发流量并不常见；如果为以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力，而不会因为突发的超负荷的请求而完全崩溃。</li>
<li><strong>可恢复性</strong><br>　　系统的一部分组件失效时，不会影响到整个系统。消息队列降低了进程间的耦合度，所以即使一个处理消息的进程挂掉，加入队列中的消息仍然可以在系统恢复后被处理。</li>
<li><strong>顺序保证</strong><br>　　在大多使用场景下，数据处理的顺序都很重要。大部分消息队列本来就是排序的，并且能保证数据会按照特定的顺序来处理。Kafka保证一个Partition内的消息的有序性。</li>
<li><strong>缓冲</strong><br>　　在任何重要的系统中，都会有需要不同的处理时间的元素。例如，加载一张图片比应用过滤器花费更少的时间。消息队列通过一个缓冲层来帮助任务最高效率的执行———写入队列的处理会尽可能的快速。该缓冲有助于控制和优化数据流经过系统的速度。</li>
<li><strong>异步通信</strong><br>　　很多时候，用户不想也不需要立即处理消息。消息队列提供了异步处理机制，允许用户把一个消息放入队列，但并不立即处理它。想向队列中放入多少消息就放多少，然后在需要的时候再去处理它们。</li>
</ul>
<h1 id="Kafka架构"><a href="#Kafka架构" class="headerlink" title="Kafka架构"></a>Kafka架构</h1><h2 id="Kafka名词术语"><a href="#Kafka名词术语" class="headerlink" title="Kafka名词术语"></a>Kafka名词术语</h2><ul>
<li>服务器：Broker, 接收客户端发送过来的消息，对消息进行持久化，进行消息的推送等等功能。</li>
<li>主题：Topic。<strong>主题是承载消息的逻辑容器</strong>，在实际使用中多用来区分具体的业务。</li>
<li>分区：Partition。一个有序的消息序列。每个主题下可以有多个分区。分区在业务上并没有太多意义。</li>
<li>消息：Message。由消息头（CRC、版本号、属性、时间戳等等）、消息键（KEY）、消息体（VALUE）、时间戳（TIMESTAMP）等组成。</li>
<li>消息位移：Offset。<strong>表示分区中每条消息的位置信息</strong>，是一个随着消息单调递增且一旦确定就不变的值（对于某条消息而言）。</li>
<li>副本：Replica。<strong>Kafka 中同一条消息能够被拷贝到多个地方以提供数据冗余，这些地方就是所谓的副本</strong>。副本还分为领导者副本（leader replica）和追随者副本（foller replica），各自有不同的角色划分。每个分区可配置多个副本实现高可用。一个分区的N个副本一定在N个不同的Broker上。</li>
<li>生产者：Producer。向主题发布新消息的应用程序。</li>
<li>消费者：Consumer。从主题订阅新消息的应用程序。</li>
<li>消费者位移：Consumer Offset。表示消费者消费进度，<strong>每个消费者都有自己的消费者位移</strong>。offset保存在broker端的内部topic中（老版本存储在zookeeper集群），不是在clients中保存。**&lt;Group ID，主题名，分区号 &gt;**</li>
<li>消费者组：Consumer Group。<strong>多个消费者实例共同组成的一个组，同时消费某个主题的多个分区</strong>以实现高吞吐。</li>
<li>重平衡：Rebalance。消费者组内某个消费者实例挂掉后，其他消费者实例自动重新分配订阅主题分区</li>
</ul>
<h3 id="topic-和-partition"><a href="#topic-和-partition" class="headerlink" title="topic 和 partition"></a>topic 和 partition</h3><img src="http://www.jasongj.com/img/kafka/KafkaColumn1/partition.png" style="zoom:100%;" />

<p>partition上的每一条消息都会被分配一个序列号，该序列号被称为位移。</p>
<p>每条消息在某个消息的位移是固定的（不要和消费者的位移产生混淆）。</p>
<p>所以，kafka中的每一条消息都可以由&lt;topic,partition,offset&gt;这个三元组位移确定。</p>
<h3 id="replica"><a href="#replica" class="headerlink" title="replica"></a>replica</h3><p>副本分为leader副本和follower副本。<br>这个主从结构并不是一回事。一般情况下，在主从结构中，主提供写，主从都可以提供读。</p>
<h2 id="Kafka拓扑结构"><a href="#Kafka拓扑结构" class="headerlink" title="Kafka拓扑结构"></a>Kafka拓扑结构</h2><p><img src="http://www.jasongj.com/img/kafka/KafkaColumn1/KafkaArchitecture.png" alt="kafka architecture 架构"></p>
<p>如上图所示，一个典型的Kafka集群中包含若干Producer（可以是web前端产生的Page View，或者是服务器日志，系统CPU、Memory等），若干broker（Kafka支持水平扩展，一般broker数量越多，集群吞吐率越高），若干Consumer Group，以及一个<a target="_blank" rel="noopener" href="http://zookeeper.apache.org/">Zookeeper</a>集群。Kafka通过Zookeeper管理集群配置，选举leader，以及在Consumer Group发生变化时进行rebalance。Producer使用push模式将消息发布到broker，Consumer使用pull模式从broker订阅并消费消息。 　　</p>
<h2 id="Topic-amp-Partition"><a href="#Topic-amp-Partition" class="headerlink" title="Topic &amp; Partition"></a>Topic &amp; Partition</h2><p>　　Topic在逻辑上可以被认为是一个queue，每条消费都必须指定它的Topic，可以简单理解为必须指明把这条消息放进哪个queue里。为了使得Kafka的吞吐率可以线性提高，物理上把Topic分成一个或多个Partition，每个Partition在物理上对应一个文件夹，该文件夹下存储这个Partition的所有消息和索引文件。若创建topic1和topic2两个topic，且分别有13个和19个分区，则整个集群上会相应会生成共32个文件夹。</p>
<p>每个日志文件都是一个<code>log entry</code>序列，每个<code>log entry</code>包含一个4字节整型数值（值为N+5），1个字节的”magic value”，4个字节的CRC校验码，其后跟N个字节的消息体。每条消息都有一个当前Partition下唯一的64字节的offset，它指明了这条消息的起始位置。磁盘上存储的消息格式如下：<br>　　message length ： 4 bytes (value: 1+4+n)<br>　　“magic” value ： 1 byte<br>　　crc ： 4 bytes<br>　　payload ： n bytes<br>这个<code>log entry</code>并非由一个文件构成，而是分成多个segment，每个segment以该segment第一条消息的offset命名并以“.kafka”为后缀。另外会有一个索引文件，它标明了每个segment下包含的<code>log entry</code>的offset范围，如下图所示。</p>
<p><img src="/Kafka-1/partition_segment.png"></p>
<p>因为每条消息都被append到该Partition中，属于顺序写磁盘，因此效率非常高（经验证，顺序写磁盘效率比随机写内存还要高，这是Kafka高吞吐率的一个很重要的保证）。</p>
<p>对于传统的message queue而言，一般会删除已经被消费的消息，而Kafka集群会保留所有的消息，无论其被消费与否。当然，因为磁盘限制，不可能永久保留所有数据（实际上也没必要），因此Kafka提供两种策略删除旧数据。一是基于时间，二是基于Partition文件大小。例如可以通过配置<code>$KAFKA_HOME/config/server.properties</code>，让Kafka删除一周前的数据，也可在Partition文件超过1GB时删除旧数据，配置如下所示。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"># The minimum age of a log file to be eligible for deletion</span><br><span class="line">log.retention.hours=168</span><br><span class="line"># The maximum size of a log segment file. When this size is reached a new log segment will be created.</span><br><span class="line">log.segment.bytes=1073741824</span><br><span class="line"># The interval at which log segments are checked to see if they can be deleted according to the retention policies</span><br><span class="line">log.retention.check.interval.ms=300000</span><br><span class="line"># If log.cleaner.enable=true is set the cleaner will be enabled and individual logs can then be marked for log compaction.</span><br><span class="line">log.cleaner.enable=false</span><br></pre></td></tr></table></figure>





<h2 id="Producer消息路由"><a href="#Producer消息路由" class="headerlink" title="Producer消息路由"></a>Producer消息路由</h2><p>Producer发送消息到broker时，会根据Paritition机制选择将其存储到哪一个Partition。</p>
<p>如果Partition机制设置合理，所有消息可以均匀分布到不同的Partition里，这样就实现了负载均衡。如果一个Topic对应一个文件，那这个文件所在的机器I&#x2F;O将会成为这个Topic的性能瓶颈，而有了Partition后，不同的消息可以并行写入不同broker的不同Partition里，极大的提高了吞吐率。可以在<code>$KAFKA_HOME/config/server.properties</code>中通过配置项<code>num.partitions</code>来指定新建Topic的默认Partition数量，也可在创建Topic时通过参数指定，同时也可以在Topic创建之后通过Kafka提供的工具修改。</p>
<p>具体的本文不再详细介绍。</p>
<h2 id="Consumer-Group"><a href="#Consumer-Group" class="headerlink" title="Consumer Group"></a>Consumer Group</h2><p>（本节所有描述都是基于Consumer hight level API而非low level API）。<br>使用Consumer high level API时，同一Topic的一条消息只能被同一个Consumer Group内的一个Consumer消费，但多个Consumer Group可同时消费这一消息。</p>
<p><a target="_blank" rel="noopener" href="http://www.jasongj.com/img/kafka/KafkaColumn1/consumer_group.png"><img src="/Kafka-1/consumer_group.png" alt="kafka consumer group"></a>
　　</p>
<p>这是Kafka用来实现一个Topic消息的广播（发给所有的Consumer）和单播（发给某一个Consumer）的手段。一个Topic可以对应多个Consumer Group。如果需要实现广播，只要每个Consumer有一个独立的Group就可以了。要实现单播只要所有的Consumer在同一个Group里。用Consumer Group还可以将Consumer进行自由的分组而不需要多次发送消息到不同的Topic。</p>
<h2 id="Push-vs-Pull"><a href="#Push-vs-Pull" class="headerlink" title="Push vs. Pull"></a>Push vs. Pull</h2><p>作为一个消息系统，Kafka遵循了传统的方式，选择由Producer向broker push消息并由Consumer从broker pull消息。一些logging-centric system，比如Facebook的<a target="_blank" rel="noopener" href="https://github.com/facebookarchive/scribe">Scribe</a>和Cloudera的<a target="_blank" rel="noopener" href="http://flume.apache.org/">Flume</a>，采用push模式。事实上，push模式和pull模式各有优劣。</p>
<p>push模式很难适应消费速率不同的消费者，因为消息发送速率是由broker决定的。push模式的目标是尽可能以最快速度传递消息，但是这样很容易造成Consumer来不及处理消息，典型的表现就是拒绝服务以及网络拥塞。而pull模式则可以根据Consumer的消费能力以适当的速率消费消息。</p>
<p>对于Kafka而言，pull模式更合适。pull模式可简化broker的设计，Consumer可自主控制消费消息的速率，同时Consumer可以自己控制消费方式——即可批量消费也可逐条消费，同时还能选择不同的提交方式从而实现不同的传输语义。 　　</p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/11/22/Linux%E7%9A%84mmap%E6%A6%82%E5%BF%B5%E3%80%81%E5%8E%9F%E7%90%86%E3%80%81%E4%BD%BF%E7%94%A8/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/11/22/Linux%E7%9A%84mmap%E6%A6%82%E5%BF%B5%E3%80%81%E5%8E%9F%E7%90%86%E3%80%81%E4%BD%BF%E7%94%A8/" class="post-title-link" itemprop="url">Linux的mmap概念、原理、使用</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-11-22 09:52:37" itemprop="dateCreated datePublished" datetime="2021-11-22T09:52:37+08:00">2021-11-22</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2022-03-28 15:27:39" itemprop="dateModified" datetime="2022-03-28T15:27:39+08:00">2022-03-28</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/Linux/" itemprop="url" rel="index"><span itemprop="name">Linux</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="mmap基础概念"><a href="#mmap基础概念" class="headerlink" title="mmap基础概念"></a>mmap基础概念</h1><p><code>mmap</code>是一种<strong>内存映射文件</strong>的方法，即<strong>将一个文件或者其它对象映射到进程的地址空间</strong>，<strong>实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系</strong>。</p>
<p>实现这样的映射关系后<strong>，进程就可以采用指针的方式读写操作这一段内存</strong>，而<strong>系统会自动回写脏页面到对应的文件磁盘上</strong>，即完成了对文件的操作而不必再调用read,write等系统调用函数。相反，内核空间对这段区域的修改也直接反映用户空间，从而可以实现<strong>不同进程间的文件共享</strong>。</p>
<p>如下图所示：</p>
<p><img src="/2021/11/22/Linux%E7%9A%84mmap%E6%A6%82%E5%BF%B5%E3%80%81%E5%8E%9F%E7%90%86%E3%80%81%E4%BD%BF%E7%94%A8/mmap1.png"></p>
<p>由上图可以看出，进程的虚拟地址空间，由多个虚拟内存区域构成。虚拟内存区域是进程的虚拟地址空间中的一个同质区间，即具有同样特性的连续地址范围。上图中所示的text数据段（代码段）、初始数据段、BSS数据段、堆、栈和内存映射，都是一个独立的虚拟内存区域。而为内存映射服务的地址空间处在堆栈之间的空余部分。</p>
<p>linux内核使用<code>vm_area_struct</code>结构来表示<strong>一个独立的虚拟内存区域</strong>，由于每个不同质的虚拟内存区域功能和内部机制都不同，因此一<strong>个进程使用多个vm_area_struct结构来分别表示不同类型的虚拟内存区域</strong>。</p>
<p>各个vm_area_struct结构使用链表或者树形结构链接，方便进程快速访问，如下图所示：</p>
<p><img src="/2021/11/22/Linux%E7%9A%84mmap%E6%A6%82%E5%BF%B5%E3%80%81%E5%8E%9F%E7%90%86%E3%80%81%E4%BD%BF%E7%94%A8/mmap2.png"></p>
<p>vm_area_struct结构中包含区域起始和终止地址以及其他相关信息，同时也包含一个vm_ops指针，其内部可引出所有针对这个区域可以使用的系统调用函数。这样，进程对某一虚拟内存区域的任何操作需要用要的信息，都可以从vm_area_struct中获得。</p>
<p><strong>mmap函数就是要创建一个新的vm_area_struct结构，并将其与文件的物理磁盘地址相连。</strong></p>
<h1 id="mmap内存映射步骤和原理"><a href="#mmap内存映射步骤和原理" class="headerlink" title="mmap内存映射步骤和原理"></a>mmap内存映射步骤和原理</h1><h2 id="内存映射步骤"><a href="#内存映射步骤" class="headerlink" title="内存映射步骤"></a>内存映射步骤</h2><p>mmap内存映射的实现过程，总的来说可以分为三个阶段：</p>
<p><strong>（一）进程启动映射过程，并在虚拟地址空间中为映射创建虚拟映射区域</strong></p>
<ol>
<li>进程在用户空间调用库函数mmap，</li>
</ol>
<p>原型：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> *<span class="title function_">mmap</span><span class="params">(<span class="type">void</span> *start, <span class="type">size_t</span> length, <span class="type">int</span> prot, <span class="type">int</span> flags, <span class="type">int</span> fd, <span class="type">off_t</span> offset)</span>;</span><br></pre></td></tr></table></figure>

<ol start="2">
<li><p>在当前进程的虚拟地址空间中，寻找一段空闲的满足要求的连续的虚拟地址;</p>
</li>
<li><p>为此虚拟区分配一个vm_area_struct结构，接着对这个结构的各个域进行了初始化</p>
</li>
<li><p>将新建的虚拟区结构（vm_area_struct）插入进程的虚拟地址区域链表或树中</p>
</li>
</ol>
<p><strong>（二）调用内核空间的系统调用函数mmap（不同于用户空间函数），实现文件物理地址和进程虚拟地址的一一映射关系</strong></p>
<p>5、为映射分配了新的虚拟地址区域后，通过待映射的文件指针，在文件描述符表中找到对应的文件描述符，通过文件描述符，链接到内核“已打开文件集”中该文件的文件结构体（struct file），每个文件结构体维护着和这个已打开文件相关各项信息。</p>
<p>6、通过该文件的文件结构体，链接到file_operations模块，调用内核函数mmap，其原型为：int mmap(struct file *filp, struct vm_area_struct *vma)，不同于用户空间库函数。</p>
<p>7、内核mmap函数通过虚拟文件系统inode模块定位到文件磁盘物理地址。</p>
<p>8、通过remap_pfn_range函数建立页表，即实现了文件地址和虚拟地址区域的映射关系。此时，这片虚拟地址并没有任何数据关联到主存中。</p>
<p><strong>（三）进程发起对这片映射空间的访问，引发缺页异常，实现文件内容到物理内存（主存）的拷贝</strong></p>
<p>注：前两个阶段仅在于创建虚拟区间并完成地址映射，但是并没有将任何文件数据的拷贝至主存。真正的文件读取是当进程发起读或写操作时。</p>
<p>9、进程的读或写操作访问虚拟地址空间这一段映射地址，通过查询页表，发现这一段地址并不在物理页面上。因为目前只建立了地址映射，真正的硬盘数据还没有拷贝到内存中，因此引发缺页异常。</p>
<p>10、缺页异常进行一系列判断，确定无非法操作后，内核发起请求调页过程。</p>
<p>11、调页过程先在交换缓存空间（swap cache）中寻找需要访问的内存页，如果没有则调用nopage函数把所缺的页从磁盘装入到主存中。</p>
<p>12、之后进程即可对这片主存进行读或者写的操作，如果写操作改变了其内容，一定时间后系统会自动回写脏页面到对应磁盘地址，也即完成了写入到文件的过程。</p>
<p>注：修改过的脏页面并不会立即更新回文件中，而是有一段时间的延迟，可以调用msync()来强制同步, 这样所写的内容就能立即保存到文件里了。</p>
<h3 id="内核怎样保证各个进程寻址到同一个共享内存区域的内存页面"><a href="#内核怎样保证各个进程寻址到同一个共享内存区域的内存页面" class="headerlink" title="内核怎样保证各个进程寻址到同一个共享内存区域的内存页面"></a>内核怎样保证各个进程寻址到同一个共享内存区域的内存页面</h3><ol>
<li><p>page cache及swap cache中页面的区分：一个被访问文件的物理页面都驻留在page cache或swap cache中，一个页面的所有信息由struct page来描述。struct page中有一个域为指针mapping ，它指向一个struct address_space类型结构。page cache或swap cache中的所有页面就是根据address_space结构以及一个偏移量来区分的。</p>
</li>
<li><p>文件与 address_space结构的对应：一个具体的文件在打开后，内核会在内存中为之建立一个struct inode结构，其中的i_mapping域指向一个address_space结构。这样，一个文件就对应一个address_space结构，一个 address_space与一个偏移量能够确定一个page cache 或swap cache中的一个页面。因此，当要寻址某个数据时，很容易根据给定的文件及数据在文件内的偏移量而找到相应的页面。</p>
</li>
<li><p><strong>进程调用mmap()时，只是在进程空间内新增了一块相应大小的缓冲区，并设置了相应的访问标识</strong>，但并没有建立进程空间到物理页面的映射。因此，第一次访问该空间时，会引发一个缺页异常。</p>
</li>
<li><p>对于共享内存映射情况，缺页异常处理程序首先在swap cache中寻找目标页（符合address_space以及偏移量的物理页），如果找到，则直接返回地址；如果没有找到，则判断该页是否在交换区 (swap area)，如果在，则执行一个换入操作；如果上述两种情况都不满足，处理程序将分配新的物理页面，并把它插入到page cache中。进程最终将更新进程页表。<br>注：对于映射普通文件情况（非共享映射），缺页异常处理程序首先会在page cache中根据address_space以及数据偏移量寻找相应的页面。如果没有找到，则说明文件数据还没有读入内存，处理程序会从磁盘读入相应的页 面，并返回相应地址，同时，进程页表也会更新。</p>
</li>
<li><p>所有进程在映射同一个共享内存区域时，情况都一样，在建立线性地址与物理地址之间的映射之后，不论进程各自的返回地址（实际上各进程返回的地址为各进程的虚拟地址，虚拟地址自然就会不一样，而它们映射的物理地址是相同的）如何，实际访问的必然是同一个共享内存区域对应的物理页面。<br>注：一个共享内存区域可以看作是特殊文件系统shm中的一个文件，shm的安装点在交换区上。</p>
</li>
</ol>
<h1 id="mmap相关函数"><a href="#mmap相关函数" class="headerlink" title="mmap相关函数"></a>mmap相关函数</h1><p><strong>函数原型</strong></p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> *<span class="title function_">mmap</span><span class="params">(<span class="type">void</span> *start, <span class="type">size_t</span> length, <span class="type">int</span> prot, <span class="type">int</span> flags, <span class="type">int</span> fd, <span class="type">off_t</span> offset)</span>;</span><br></pre></td></tr></table></figure>

<p><strong>返回说明</strong></p>
<p>成功执行时，mmap()返回被映射区的指针;</p>
<p>失败时，mmap()返回MAP_FAILED[其值为(void *)]， error被设为以下的某个值：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"> 1 EACCES：访问出错</span><br><span class="line"> 2 EAGAIN：文件已被锁定，或者太多的内存已被锁定</span><br><span class="line"> 3 EBADF：fd不是有效的文件描述词</span><br><span class="line"> 4 EINVAL：一个或者多个参数无效</span><br><span class="line"> 5 ENFILE：已达到系统对打开文件的限制</span><br><span class="line"> 6 ENODEV：指定文件所在的文件系统不支持内存映射</span><br><span class="line"> 7 ENOMEM：内存不足，或者进程已超出最大内存映射数量</span><br><span class="line"> 8 EPERM：权能不足，操作不允许</span><br><span class="line"> 9 ETXTBSY：已写的方式打开文件，同时指定MAP_DENYWRITE标志</span><br><span class="line">10 SIGSEGV：试着向只读区写入</span><br><span class="line">11 SIGBUS：试着访问不属于进程的内存区</span><br></pre></td></tr></table></figure>



<p><strong>参数</strong></p>
<p>start：映射区的开始地址</p>
<p>length：映射区的长度</p>
<p>prot：期望的内存保护标志，不能与文件的打开模式冲突, 是以下的某个值，可以通过or运算合理地组合在一起</p>
<table>
<thead>
<tr>
<th>prot参数</th>
<th>说明</th>
</tr>
</thead>
<tbody><tr>
<td>PROT_READ</td>
<td>内存段可读</td>
</tr>
<tr>
<td>PROT_WRITE</td>
<td>内存段可写</td>
</tr>
<tr>
<td>PROT_EXEC</td>
<td>内存段可执行</td>
</tr>
<tr>
<td>PROT_NONE</td>
<td>内存段不能被访问</td>
</tr>
</tbody></table>
<p>flags：指定映射对象的类型，映射选项和映射页是否可以共享。它的值可以是一个或者多个以下位的组合体</p>
<table>
<thead>
<tr>
<th>flags参数</th>
<th>说明</th>
</tr>
</thead>
<tbody><tr>
<td>MAP_SHARED</td>
<td>进程间共享内存，对该内存段修改反映到映射文件中。提供了POSIX共享内存</td>
</tr>
<tr>
<td>MAP_PRIVATE</td>
<td>内存段为调用进程所私有。对该内存段的修改不会反映到映射文件</td>
</tr>
<tr>
<td>MAP_ANNOYMOUS</td>
<td>这段内存不是从文件映射而来的。内容被初始化为全0</td>
</tr>
<tr>
<td>MAP_FIXED</td>
<td>内存段必须位于start参数指定的地址处，start必须是页大小的整数倍（4K整数倍）</td>
</tr>
<tr>
<td>MAP_HUGETLB</td>
<td>按照大内存页面来分配内存空间</td>
</tr>
</tbody></table>
<p>fd：有效的文件描述符。如果MAP_ANONYMOUS被设定，为了兼容问题，其值应为-1；</p>
<p>offset：被映射对象内容的起点；</p>
<p><strong>相关函数</strong></p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">munmap</span><span class="params">( <span class="type">void</span> * addr, <span class="type">size_t</span> len )</span> </span><br></pre></td></tr></table></figure>

<p>成功执行时，munmap()返回0。失败时，munmap返回-1，error返回标志和mmap一致；</p>
<p>该调用在进程地址空间中解除一个映射关系，addr是调用mmap()时返回的地址，len是映射区的大小；</p>
<p>当映射关系解除后，对原来映射地址的访问将导致段错误发生。 </p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">msync</span><span class="params">( <span class="type">void</span> *addr, <span class="type">size_t</span> len, <span class="type">int</span> flags )</span></span><br></pre></td></tr></table></figure>

<p>一般说来，进程在映射空间的对共享内容的改变并不直接写回到磁盘文件中，往往在调用munmap()后才执行该操作。</p>
<p>可以通过调用msync()实现磁盘上文件内容与共享内存区的内容一致, 也就是内存同步到磁盘上。</p>
<h1 id="mmap和常规文件操作的区别"><a href="#mmap和常规文件操作的区别" class="headerlink" title="mmap和常规文件操作的区别"></a>mmap和常规文件操作的区别</h1><p>参考文章<a target="_blank" rel="noopener" href="http://47.117.127.179/2021/11/20/Linux%E6%96%87%E4%BB%B6%E6%8F%8F%E8%BF%B0%E7%AC%A6%E7%9A%84%E5%AE%9E%E7%8E%B0/">《Linux文件描述符的使用与实现》</a>。</p>
<p>我们首先简单的回顾一下常规文件系统操作（调用read&#x2F;fread等类函数）中，函数的调用过程：</p>
<ol>
<li><p>进程发起读文件请求;</p>
</li>
<li><p>内核通过查找进程文件符表（<code>fd_array</code>），定位到内核已打开文件集上的文件信息，从而找到此文件的<code>inode</code>;</p>
</li>
<li><p>inode在address_space上查找要请求的文件页是否已经缓存在页缓存中。如果存在，则直接返回这片文件页的内容。</p>
</li>
<li><p>如果不存在，则通过inode定位到文件磁盘地址，将数据从磁盘复制到页缓存。之后再次发起读页面过程，进而将页缓存中的数据发给用户进程。</p>
</li>
</ol>
<p>总结来说，常规文件操作为了提高读写效率和保护磁盘，使用了<strong>页缓存机制</strong>。</p>
<p>这样造成读文件时<strong>需要先将文件页从磁盘拷贝到页缓存中</strong>，由于<strong>页缓存处在内核空间</strong>，不能被用户进程直接寻址，所以<strong>还需要将页缓存中数据页再次拷贝到内存对应的用户空间中</strong>。这样，<strong>通过了两次数据拷贝过程，才能完成进程对文件内容的获取任务</strong>。写操作也是一样，待写入的buffer在内核空间不能直接访问，必须要先拷贝至内核空间对应的主存，再写回磁盘中（延迟写回），也是需要两次数据拷贝。</p>
<p>而使用mmap操作文件时，创建新的虚拟内存区域和建立文件磁盘地址和虚拟内存区域映射这两步，没有任何文件拷贝操作，而之后<strong>访问数据时发现内存中并无数据而发起的缺页异常过程</strong>，<strong>可以通过已经建立好的映射关系，只使用一次数据拷贝，就从磁盘中将数据传入内存的用户空间中</strong>，供进程使用。</p>
<p><strong>总而言之，常规文件操作需要从磁盘到页缓存再到用户主存的两次数据拷贝，</strong></p>
<p><strong>而mmap操控文件，只需要从磁盘到用户主存的一次数据拷贝过程。</strong></p>
<p>说白了，mmap的关键点是<strong>实现了用户空间和内核空间的数据直接交互而省去了空间不同数据不通的繁琐过程</strong>。因此mmap效率更高。</p>
<h1 id="mmap优点"><a href="#mmap优点" class="headerlink" title="mmap优点"></a>mmap优点</h1><p>由上文讨论可知，mmap优点共有一下几点：</p>
<ol>
<li><p><strong>对文件的读取操作跨过了页缓存</strong>，减少了数据的拷贝次数，<strong>用内存读写取代I&#x2F;O读写</strong>，提高了文件读取效率。</p>
</li>
<li><p>实现了用户空间和内核空间的高效交互方式。<strong>两空间的各自修改操作可以直接反映在映射的区域内，从而被对方空间及时捕捉</strong>。</p>
</li>
<li><p>提供<strong>进程间共享内存及相互通信</strong>的方式。不管是父子进程还是无亲缘关系的进程，都可以<strong>将自身用户空间映射到同一个文件或匿名映射到同一片区域</strong>。从而通过各自对映射区域的改动，达到进程间通信和进程间共享的目的。同时，如果进程A和进程B都映射了区域C，当A第一次读取C时通过缺页从磁盘复制文件页到内存中；但当B再读C的相同页面时，虽然也会产生缺页异常，但是不再需要从磁盘中复制文件过来，而可直接使用已经保存在内存中的文件数据。</p>
</li>
<li><p><strong>可用于实现高效的大规模数据传输</strong>。内存空间不足，是制约大数据操作的一个方面，解决方案往往是借助硬盘空间协助操作，补充内存的不足。但是进一步会造成大量的文件I&#x2F;O操作，极大影响效率。这个问题可以通过mmap映射很好的解决。换句话说，但凡是需要用磁盘空间代替内存的时候，mmap都可以发挥其功效。</p>
</li>
</ol>
<h1 id="mmap用途"><a href="#mmap用途" class="headerlink" title="mmap用途"></a>mmap用途</h1><ol>
<li><p>共享内存：可以说是最有用的进程间通信方式，也是最快的IPC形式。两个不同进程A、B共享内存的意思是，同一块物理内存被映射到进程A、B各自的进程地址空 间。进程A可以即时看到进程B对共享内存中数据的更新，反之亦然。由于多个进程共享同一块内存区域，必然需要某种同步机制，互斥锁和信号量都可以。</p>
<p>Linux的2.2.x内核支持多种共享内存方式，如<strong>1) mmap()系统调用，2) Posix共享内存，以及3) System V共享内存</strong>。linux发行版本如Redhat 8.0支持mmap()系统调用及System V共享内存，但还没实现Posix共享内存。</p>
</li>
<li><p>文件访问方式：mmap函数是unix&#x2F;linux下的系统调用，mmap系统调用并不是完全为了用于共享内存而设计的。它本身提供了不同于一般对普通文件的访问方式，进程可以像读写内存一样对普通文件的操作。而Posix或系统V的共享内存IPC则纯粹用于共享目的，当然mmap()实现共享内存也是其主要应用之一。</p>
</li>
</ol>
<p>mmap系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后，进程可以像访问普通内存一样对文件进行访问，不必再调用read()，write()等操作。<br>我们的程序中大量运用了mmap，用到的正是mmap的这种“像访问普通内存一样对文件进行访问”的功能。实践证明，<strong>当要对一个文件频繁的进行访问，并且指针来回移动时</strong>，调用mmap比用常规的方法快很多</p>
<h1 id="mmap使用细节"><a href="#mmap使用细节" class="headerlink" title="mmap使用细节"></a>mmap使用细节</h1><ol>
<li><p>使用mmap需要注意的一个关键点是，<strong>mmap映射区域大小必须是物理页大小(page_size)的整倍数</strong>（32位系统中通常是4k字节）。原因是，内存的最小粒度是页，而进程虚拟地址空间和内存的映射也是以页为单位。为了匹配内存的操作，mmap从磁盘到虚拟地址空间的映射也必须是页。</p>
</li>
<li><p>内核可以跟踪被内存映射的底层对象（文件）的大小，<strong>进程可以合法的访问在当前文件大小以内且在内存映射区以内的那些字节</strong>。也就是说，<strong>如果文件的大小一直在扩张，只要在映射区域范围内的数据，进程都可以合法得到</strong>，这和映射建立时文件的大小无关。具体情形参见<em>情形三</em>。</p>
</li>
<li><p><strong>映射建立之后，即使文件关闭，映射依然存在</strong>。因为映射的是磁盘的地址，不是文件本身，和文件句柄无关。同时可用于进程间通信的有效地址空间不完全受限于被映射文件的大小，因为是按页映射。</p>
</li>
</ol>
<p>在上面的知识前提下，我们下面看看如果大小不是页的整倍数的具体情况：</p>
<p><strong>情形一：一个文件的大小是5000字节，mmap函数从一个文件的起始位置开始，映射5000字节到虚拟内存中。</strong></p>
<p>分析：因为单位物理页面的大小是4096字节，虽然被映射的文件只有5000字节，但是对应到进程虚拟地址区域的大小需要满足整页大小，因此mmap函数执行后，实际映射到虚拟内存区域8192个 字节，5000~8191的字节部分用零填充。映射后的对应关系如下图所示：</p>
<p>​        <img src="/2021/11/22/Linux%E7%9A%84mmap%E6%A6%82%E5%BF%B5%E3%80%81%E5%8E%9F%E7%90%86%E3%80%81%E4%BD%BF%E7%94%A8/mmap3.png" alt="img"></p>
<p>此时：</p>
<p>（1）读&#x2F;写前5000个字节（0~4999），会返回操作文件内容。</p>
<p>（2）读字节5000-8191时，结果全为0。写5000~8191时，进程不会报错，但是所写的内容不会写入原文件中 。</p>
<p>（3）读&#x2F;写8192以外的磁盘部分，会返回一个<code>SIGSECV</code>错误（无效的内存引用）。</p>
<p><strong>情形二：一个文件的大小是5000字节，mmap函数从一个文件的起始位置开始，映射15000字节到虚拟内存中，即映射大小超过了原始文件的大小。</strong></p>
<p>分析：由于文件的大小是5000字节，和情形一一样，其对应的两个物理页。那么这两个物理页都是合法可以读写的，只是超出5000的部分不会体现在原文件中。由于程序要求映射15000字节，而文件只占两个物理页，因此8192字节~15000字节都不能读写，操作时会返回异常。如下图所示：</p>
<p>​         <img src="/2021/11/22/Linux%E7%9A%84mmap%E6%A6%82%E5%BF%B5%E3%80%81%E5%8E%9F%E7%90%86%E3%80%81%E4%BD%BF%E7%94%A8/mmap4.png" alt="img"></p>
<p>此时：</p>
<p>（1）进程可以正常读&#x2F;写被映射的前5000字节(0~4999)，写操作的改动会在一定时间后反映在原文件中。</p>
<p>（2）对于5000~8191字节，进程可以进行读写过程，不会报错。但是内容在写入前均为0，另外，写入后不会反映在文件中。</p>
<p>（3）对于8192~14999字节，进程不能对其进行读写，会报<code>SIGBUS</code>错误（指针所对应的地址是有效地址，但总线不能正常使用该 指针，通常是未对齐的数据访问所致）。</p>
<p>（4）对于15000以外的字节，进程不能对其读写，会引发<code>SIGSEGV</code>错误。</p>
<p><strong>情形三：一个文件初始大小为0，使用mmap操作映射了1000*4K的大小，即1000个物理页大约4M字节空间，mmap返回指针ptr。</strong></p>
<p>分析：如果在映射建立之初，就对文件进行读写操作，由于文件大小为0，并没有合法的物理页对应，如同情形二一样，会返回SIGBUS错误。</p>
<p>但是如果每次操作ptr读写前，<strong>先增加文件的大小？</strong>，那么ptr在文件大小内部的操作就是合法的。例如，文件扩充4096字节，ptr就能操作ptr  [ (char)ptr + 4095]的空间。只要文件扩充的范围在1000个物理页（映射范围）内，ptr都可以对应操作相同的大小。</p>
<p>这样，方便随时扩充文件空间，随时写入文件，不造成空间浪费。</p>
<blockquote>
<p>mmap针对Windows和Unix的版本在具体实现上有所不同，对于Windows版本，当length参数比file本身size大的时候，会自动扩展file为指定length大小；而Unix版本不支持自动扩展，即length只能小于等于size of file，如果超出size，则会报错。</p>
</blockquote>
<p><a target="_blank" rel="noopener" href="https://www.cnblogs.com/alantu2018/p/8506381.html">https://www.cnblogs.com/alantu2018/p/8506381.html</a></p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/11/20/Linux%E6%96%87%E4%BB%B6%E6%8F%8F%E8%BF%B0%E7%AC%A6%E7%9A%84%E5%AE%9E%E7%8E%B0/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/11/20/Linux%E6%96%87%E4%BB%B6%E6%8F%8F%E8%BF%B0%E7%AC%A6%E7%9A%84%E5%AE%9E%E7%8E%B0/" class="post-title-link" itemprop="url">Linux文件描述符的使用与实现</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-11-20 22:15:56" itemprop="dateCreated datePublished" datetime="2021-11-20T22:15:56+08:00">2021-11-20</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2022-03-28 15:28:41" itemprop="dateModified" datetime="2022-03-28T15:28:41+08:00">2022-03-28</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/Linux/" itemprop="url" rel="index"><span itemprop="name">Linux</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="什么是文件描述符"><a href="#什么是文件描述符" class="headerlink" title="什么是文件描述符"></a>什么是文件描述符</h1><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>文件描述符在形式上是一个非负整数。实际上，它是一个索引值，指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时，内核向进程返回一个文件描述符。在程序设计程序设计)中，一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。</p>
<p>每个Unix进程（除了可能的守护进程）应均有三个标准的POSIX文件描述符，对应于三个标准流：</p>
<table>
<thead>
<tr>
<th align="center">整数值</th>
<th align="center">名称</th>
<th align="center">符号常量</th>
<th align="center">文件流</th>
</tr>
</thead>
<tbody><tr>
<td align="center">0</td>
<td align="center"><a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/Stdin">Standard input</a></td>
<td align="center">STDIN_FILENO</td>
<td align="center">stdin</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center"><a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/Stdout">Standard output</a></td>
<td align="center">STDOUT_FILENO</td>
<td align="center">stdout</td>
</tr>
<tr>
<td align="center">2</td>
<td align="center"><a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/Stderr">Standard error</a></td>
<td align="center">STDERR_FILENO</td>
<td align="center">stderr</td>
</tr>
</tbody></table>
<p>文件描述符的优点主要有两个：</p>
<ul>
<li>基于文件描述符的I&#x2F;O操作兼容POSIX标准。</li>
<li>在UNIX、Linux的系统调用中，大量的系统调用都是依赖于文件描述符。</li>
</ul>
<p>例如，下面的代码就示范了如何基于文件描述符来读取当前目录下的一个指定文件，并把文件内容打印至Console中。</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/types.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/stat.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;fcntl.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span> <span class="params">(<span class="type">void</span>)</span>&#123;</span><br><span class="line">    <span class="type">int</span> fd;</span><br><span class="line">    <span class="type">int</span> numbytes;</span><br><span class="line">    <span class="type">char</span> path[] = <span class="string">&quot;file&quot;</span>;</span><br><span class="line">    <span class="type">char</span> buf[<span class="number">256</span>];</span><br><span class="line"></span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     * O_CREAT:如果文件不存在则创建</span></span><br><span class="line"><span class="comment">     * O_RDONLY:以只读模式打开文件</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    fd = open(path, O_CREAT | O_RDONLY, <span class="number">0644</span>);</span><br><span class="line">    <span class="keyword">if</span>(fd &lt; <span class="number">0</span>)&#123;</span><br><span class="line">        perror(<span class="string">&quot;open()&quot;</span>);</span><br><span class="line">        <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">memset</span>(buf, <span class="number">0x00</span>, <span class="number">256</span>);</span><br><span class="line">    <span class="keyword">while</span>((numbytes = read(fd, buf, <span class="number">255</span>)) &gt; <span class="number">0</span>)&#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;%d bytes read: %s&quot;</span>, numbytes, buf);</span><br><span class="line">        <span class="built_in">memset</span>(buf, <span class="number">0x00</span>, <span class="number">256</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    close (fd);</span><br><span class="line">    <span class="built_in">exit</span>(EXIT_SUCCESS);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>




<h2 id="与文件描述符相关的操作"><a href="#与文件描述符相关的操作" class="headerlink" title="与文件描述符相关的操作"></a>与文件描述符相关的操作</h2><h3 id="文件描述符的生成"><a href="#文件描述符的生成" class="headerlink" title="文件描述符的生成"></a>文件描述符的生成</h3><ul>
<li>open(), open64(), creat(), creat64()</li>
<li>socket()</li>
<li>socketpair()</li>
<li>pipe()</li>
</ul>
<h3 id="与单一文件描述符相关的操作"><a href="#与单一文件描述符相关的操作" class="headerlink" title="与单一文件描述符相关的操作"></a>与单一文件描述符相关的操作</h3><ul>
<li>read(), write()</li>
<li>recv(), send()</li>
<li>recvmsg(), sendmsg()</li>
<li>sendfile()</li>
<li>lseek(), lseek64()</li>
<li>fstat(), fstat64()</li>
<li>fchmod()</li>
<li>fchown()</li>
</ul>
<h3 id="与复数文件描述符相关的操作"><a href="#与复数文件描述符相关的操作" class="headerlink" title="与复数文件描述符相关的操作"></a>与复数文件描述符相关的操作</h3><ul>
<li>select(), pselect()</li>
<li>poll(),epoll()</li>
</ul>
<h3 id="与文件描述符表相关的操作"><a href="#与文件描述符表相关的操作" class="headerlink" title="与文件描述符表相关的操作"></a>与文件描述符表相关的操作</h3><ul>
<li>close()</li>
<li>dup()</li>
<li>dup2()</li>
<li>fcntl (F_DUPFD)</li>
<li>fcntl (F_GETFD and F_SETFD)</li>
</ul>
<h3 id="改变进程状态的操作"><a href="#改变进程状态的操作" class="headerlink" title="改变进程状态的操作"></a>改变进程状态的操作</h3><ul>
<li>fchdir()</li>
<li>mmap()</li>
</ul>
<h3 id="与文件加锁的操作"><a href="#与文件加锁的操作" class="headerlink" title="与文件加锁的操作"></a>与文件加锁的操作</h3><ul>
<li>flock()</li>
<li>fcntl (F_GETLK, F_SETLK and F_SETLKW)</li>
<li>lockf()</li>
</ul>
<h3 id="与套接字相关的操作"><a href="#与套接字相关的操作" class="headerlink" title="与套接字相关的操作"></a>与套接字相关的操作</h3><ul>
<li>connect()</li>
<li>bind()</li>
<li>listen()</li>
<li>accept()</li>
<li>getsockname()</li>
<li>getpeername()</li>
<li>getsockopt(), setsockopt()</li>
<li>shutdown()</li>
</ul>
<h1 id="文件描述符的实现"><a href="#文件描述符的实现" class="headerlink" title="文件描述符的实现"></a>文件描述符的实现</h1><p><strong>在内核中每一个打开的文件都需要由3种数据结构来进行维护</strong>。</p>
<p> 根据文中内容，这三种数据结构分别为：</p>
<ol>
<li><p>进程级：每个进程对应一张<strong>打开文件描述符表，</strong>这是<strong>进程级</strong>数据结构，也就是每一个进程都各自有这样一个数据结构；</p>
</li>
<li><p>内核级：内核维持一张<strong>打开文件表，文件表由多个文件表项组成，</strong>这是<strong>系统级</strong>数据结构，也就是说这样的数据结构是针对于整个内核而言的，每个进程都可共享的；</p>
</li>
<li><p>系统级：每个打开的文件对应一个<strong>i节点（i-node）</strong>数据结构（Linux下只有i节点没有v节点），由于这是每一个打开的文件与之对应的，因此这也是一个<strong>系统级</strong>数据结构，存在于内核中，非进程所独有。</p>
</li>
</ol>
<p>那么，这三种数据结构到底是什么呢？</p>
<h2 id="文件描述符表"><a href="#文件描述符表" class="headerlink" title="文件描述符表"></a>文件描述符表</h2><p>在Linux中，对于每一个进程，都会分配一个PCB数据结构，它其中包含了该进程的所有信息，而在代码实现上，这个数据结构名为<strong>task_struct</strong>，在linux源码的<strong>include&#x2F;linux&#x2F;sched.h</strong>中可以找到其定义，它是一个很庞大的结构体，部分定义如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span> &#123;</span></span><br><span class="line">	......</span><br><span class="line">	<span class="comment">/* CPU-specific state of this task */</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">thread_struct</span> <span class="title">thread</span>;</span></span><br><span class="line">	<span class="comment">/* filesystem information */</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">fs_struct</span> *<span class="title">fs</span>;</span></span><br><span class="line">	<span class="comment">/* open file information */</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">files_struct</span> *<span class="title">files</span>;</span>  <span class="comment">// 注意这个</span></span><br><span class="line">	<span class="comment">/* namespaces */</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">nsproxy</span> *<span class="title">nsproxy</span>;</span></span><br><span class="line">	<span class="comment">/* signal handlers */</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">signal_struct</span> *<span class="title">signal</span>;</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">sighand_struct</span> *<span class="title">sighand</span>;</span></span><br><span class="line">	......</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>



<p> 在<strong>task_struct</strong>中，有一个成员变量名为<strong>files</strong>，注释中提到这是一个描述打开文件信息的变量，其类型为<strong>struct files_struct *<strong>，那么现在再接着看</strong>files_struct</strong>，在linux源码的<strong>include&#x2F;linux&#x2F;fdtable.h</strong>中可以找到其定义，定义如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">files_struct</span> &#123;</span></span><br><span class="line">	<span class="comment">/*</span></span><br><span class="line"><span class="comment">   * read mostly part</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">	<span class="type">atomic_t</span> count;</span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">fdtable</span> __<span class="title">rcu</span> *<span class="title">fdt</span>;</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">fdtable</span> <span class="title">fdtab</span>;</span></span><br><span class="line">	<span class="comment">/*</span></span><br><span class="line"><span class="comment">   * written part on a separate cache line in SMP</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">	<span class="type">spinlock_t</span> file_lock ____cacheline_aligned_in_smp;</span><br><span class="line">	<span class="type">int</span> next_fd;</span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">embedded_fd_set</span> <span class="title">close_on_exec_init</span>;</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">embedded_fd_set</span> <span class="title">open_fds_init</span>;</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">file</span> __<span class="title">rcu</span> * <span class="title">fd_array</span>[<span class="title">NR_OPEN_DEFAULT</span>];</span></span><br><span class="line">	<span class="comment">//进程级打开文件描述符表</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>



<p>而这里所要找到的<strong>打开文件描述符表</strong>，实际上就是files_struct 中的成员<strong>struct file * fd_array[NR_OPEN_DEFAULT]<strong>它是一个指针数组，数组每一个元素都是一个指向</strong>file</strong>类型的指针，可想而知，这些指针都会指向一个打开的文件，并且file这一数据结构就是用来描述一个打开的文件的，<strong>而我们所说的文件描述符，实际上就是这个指针数组的索引。</strong>这也是为什么文件描述符是非负整数。</p>
<h2 id="文件表项"><a href="#文件表项" class="headerlink" title="文件表项"></a>文件表项</h2><p>前面说了，每一个打开文件实际上就是用一个<strong>file</strong>结构体进行描述的，在linux源码的<strong>include&#x2F;linux&#x2F;fs.h</strong>中可以找到其定义，定义如下:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">file</span> &#123;</span></span><br><span class="line">	<span class="comment">/*</span></span><br><span class="line"><span class="comment">	 * fu_list becomes invalid after file_free is called and queued via</span></span><br><span class="line"><span class="comment">	 * fu_rcuhead for RCU freeing</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	<span class="class"><span class="keyword">union</span> &#123;</span></span><br><span class="line">		<span class="class"><span class="keyword">struct</span> <span class="title">list_head</span>	<span class="title">fu_list</span>;</span></span><br><span class="line">		<span class="class"><span class="keyword">struct</span> <span class="title">rcu_head</span> 	<span class="title">fu_rcuhead</span>;</span></span><br><span class="line">	&#125;f_u;</span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">path</span>		<span class="title">f_path</span>;</span></span><br><span class="line">	<span class="comment">//文件路径，包括目录项以及i-node</span></span><br><span class="line">	<span class="meta">#<span class="keyword">define</span> f_dentry	f_path.dentry</span></span><br><span class="line">	<span class="meta">#<span class="keyword">define</span> f_vfsmnt	f_path.mnt</span></span><br><span class="line">		<span class="type">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">file_operations</span>	*<span class="title">f_op</span>;</span></span><br><span class="line">	<span class="type">spinlock_t</span>		f_lock;</span><br><span class="line">	<span class="comment">/* f_ep_links, f_flags, no IRQ */</span></span><br><span class="line">	<span class="meta">#<span class="keyword">ifdef</span> CONFIG_SMP</span></span><br><span class="line">		<span class="type">int</span>			f_sb_list_cpu;</span><br><span class="line">	<span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">		<span class="type">atomic_long_t</span>		f_count;</span><br><span class="line">	<span class="comment">//文件打开次数</span></span><br><span class="line">	<span class="type">unsigned</span> <span class="type">int</span> 		f_flags;</span><br><span class="line">	<span class="comment">//文件打开时的flag，对应于open函数的flag参数</span></span><br><span class="line">	<span class="type">fmode_t</span>			f_mode;</span><br><span class="line">	<span class="comment">//文件打开时的mode，对应于open函数的mode参数</span></span><br><span class="line">	<span class="type">loff_t</span>			f_pos;</span><br><span class="line">	<span class="comment">//文件偏移位置</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">fown_struct</span>	<span class="title">f_owner</span>;</span></span><br><span class="line">	<span class="type">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">cred</span>	*<span class="title">f_cred</span>;</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">file_ra_state</span>	<span class="title">f_ra</span>;</span></span><br><span class="line">	u64			f_version;</span><br><span class="line">	<span class="meta">#<span class="keyword">ifdef</span> CONFIG_SECURITY</span></span><br><span class="line">		<span class="type">void</span>			*f_security;</span><br><span class="line">	<span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">	<span class="comment">/* needed for tty driver, and maybe others */</span></span><br><span class="line">	<span class="type">void</span>			*private_data;</span><br><span class="line">	<span class="meta">#<span class="keyword">ifdef</span> CONFIG_EPOLL</span></span><br><span class="line">	<span class="comment">/* Used by fs/eventpoll.c to link all the hooks to this file */</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">list_head</span>	<span class="title">f_ep_links</span>;</span></span><br><span class="line">	<span class="meta">#<span class="keyword">endif</span> </span></span><br><span class="line">	<span class="comment">/* #ifdef CONFIG_EPOLL */</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">address_space</span>	*<span class="title">f_mapping</span>;</span></span><br><span class="line">	<span class="meta">#<span class="keyword">ifdef</span> CONFIG_DEBUG_WRITECOUNT</span></span><br><span class="line">		<span class="type">unsigned</span> <span class="type">long</span> f_mnt_write_state;</span><br><span class="line">	<span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>





<p>在file结构体中，不得不再说一下它的f_path成员，这是一个<strong>struct path</strong>类型的变量，该类型定义于<strong>include&#x2F;linux&#x2F;path.h：</strong></p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">path</span> &#123;</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">vfsmount</span> *<span class="title">mnt</span>;</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">dentry</span> *<span class="title">dentry</span>;</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>



<p> 这里的dentry实际上就指向文件所在的<strong>目录项</strong>了，<strong>struct dentry</strong>的类型定义于<strong>include&#x2F;linux&#x2F;dcache.h：</strong></p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">dentry</span> &#123;</span></span><br><span class="line">	<span class="comment">/* RCU lookup touched fields */</span></span><br><span class="line">	<span class="type">unsigned</span> <span class="type">int</span> d_flags;</span><br><span class="line">	<span class="comment">/* protected by d_lock */</span></span><br><span class="line">	<span class="type">seqcount_t</span> d_seq;</span><br><span class="line">	<span class="comment">/* per dentry seqlock */</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">hlist_bl_node</span> <span class="title">d_hash</span>;</span></span><br><span class="line">	<span class="comment">/* lookup hash list */</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">dentry</span> *<span class="title">d_parent</span>;</span></span><br><span class="line">	<span class="comment">/* parent directory */</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">qstr</span> <span class="title">d_name</span>;</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">inode</span> *<span class="title">d_inode</span>;</span> <span class="comment">// 注意这个</span></span><br><span class="line">	<span class="comment">/* Where the name belongs to - NULL is</span></span><br><span class="line"><span class="comment">					 * negative */</span></span><br><span class="line">	<span class="type">unsigned</span> <span class="type">char</span> d_iname[DNAME_INLINE_LEN];</span><br><span class="line">	<span class="comment">/* small names */</span></span><br><span class="line">	<span class="comment">/* Ref lookup also touches following */</span></span><br><span class="line">	<span class="type">unsigned</span> <span class="type">int</span> d_count;</span><br><span class="line">	<span class="comment">/* protected by d_lock */</span></span><br><span class="line">	<span class="type">spinlock_t</span> d_lock;</span><br><span class="line">	<span class="comment">/* per dentry lock */</span></span><br><span class="line">	<span class="type">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">dentry_operations</span> *<span class="title">d_op</span>;</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">super_block</span> *<span class="title">d_sb</span>;</span></span><br><span class="line">	<span class="comment">/* The root of the dentry tree */</span></span><br><span class="line">	<span class="type">unsigned</span> <span class="type">long</span> d_time;</span><br><span class="line">	<span class="comment">/* used by d_revalidate */</span></span><br><span class="line">	<span class="type">void</span> *d_fsdata;</span><br><span class="line">	<span class="comment">/* fs-specific data */</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">list_head</span> <span class="title">d_lru</span>;</span></span><br><span class="line">	<span class="comment">/* LRU list */</span></span><br><span class="line">	<span class="comment">/*</span></span><br><span class="line"><span class="comment">	 * d_child and d_rcu can share memory</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	<span class="class"><span class="keyword">union</span> &#123;</span></span><br><span class="line">		<span class="class"><span class="keyword">struct</span> <span class="title">list_head</span> <span class="title">d_child</span>;</span></span><br><span class="line">		<span class="comment">/* child of parent list */</span></span><br><span class="line">		<span class="class"><span class="keyword">struct</span> <span class="title">rcu_head</span> <span class="title">d_rcu</span>;</span></span><br><span class="line">	&#125;d_u;</span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">list_head</span> <span class="title">d_subdirs</span>;</span></span><br><span class="line">	<span class="comment">/* our children */</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">list_head</span> <span class="title">d_alias</span>;</span></span><br><span class="line">	<span class="comment">/* inode alias list */</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>在dentry结构体中，描述了根结点、父节点等等信息，尤其还要注意的是<strong>struct inode *d_inode</strong>这一变量，它则是指向了一个<strong>i-node</strong>结点。</p>
<p>再回到file结构体中，有一个<strong>struct file_operations *f_op</strong>变量，其类型定义在<strong>include&#x2F;linux&#x2F;fs.h</strong>中：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">file_operations</span> &#123;</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">module</span> *<span class="title">owner</span>;</span></span><br><span class="line">	<span class="type">loff_t</span> (*llseek) (<span class="keyword">struct</span> file *, <span class="type">loff_t</span>, <span class="type">int</span>);</span><br><span class="line">	<span class="type">ssize_t</span> (*read) (<span class="keyword">struct</span> file *, <span class="type">char</span> __user *, <span class="type">size_t</span>, <span class="type">loff_t</span> *);</span><br><span class="line">	<span class="type">ssize_t</span> (*write) (<span class="keyword">struct</span> file *, <span class="type">const</span> <span class="type">char</span> __user *, <span class="type">size_t</span>, <span class="type">loff_t</span> *);</span><br><span class="line">	<span class="type">ssize_t</span> (*aio_read) (<span class="keyword">struct</span> kiocb *, <span class="type">const</span> <span class="keyword">struct</span> iovec *, <span class="type">unsigned</span> <span class="type">long</span>, <span class="type">loff_t</span>);</span><br><span class="line">	<span class="type">ssize_t</span> (*aio_write) (<span class="keyword">struct</span> kiocb *, <span class="type">const</span> <span class="keyword">struct</span> iovec *, <span class="type">unsigned</span> <span class="type">long</span>, <span class="type">loff_t</span>);</span><br><span class="line">	<span class="type">int</span> (*readdir) (<span class="keyword">struct</span> file *, <span class="type">void</span> *, <span class="type">filldir_t</span>);</span><br><span class="line">	<span class="type">unsigned</span> <span class="title function_">int</span> <span class="params">(*poll)</span> <span class="params">(<span class="keyword">struct</span> file *, <span class="keyword">struct</span> poll_table_struct *)</span>;</span><br><span class="line">	<span class="type">long</span> (*unlocked_ioctl) (<span class="keyword">struct</span> file *, <span class="type">unsigned</span> <span class="type">int</span>, <span class="type">unsigned</span> <span class="type">long</span>);</span><br><span class="line">	<span class="type">long</span> (*compat_ioctl) (<span class="keyword">struct</span> file *, <span class="type">unsigned</span> <span class="type">int</span>, <span class="type">unsigned</span> <span class="type">long</span>);</span><br><span class="line">	<span class="type">int</span> (*mmap) (<span class="keyword">struct</span> file *, <span class="keyword">struct</span> vm_area_struct *);</span><br><span class="line">	<span class="type">int</span> (*open) (<span class="keyword">struct</span> inode *, <span class="keyword">struct</span> file *);</span><br><span class="line">	<span class="type">int</span> (*flush) (<span class="keyword">struct</span> file *, <span class="type">fl_owner_t</span> id);</span><br><span class="line">	<span class="type">int</span> (*release) (<span class="keyword">struct</span> inode *, <span class="keyword">struct</span> file *);</span><br><span class="line">	<span class="type">int</span> (*fsync) (<span class="keyword">struct</span> file *, <span class="type">int</span> datasync);</span><br><span class="line">	<span class="type">int</span> (*aio_fsync) (<span class="keyword">struct</span> kiocb *, <span class="type">int</span> datasync);</span><br><span class="line">	<span class="type">int</span> (*fasync) (<span class="type">int</span>, <span class="keyword">struct</span> file *, <span class="type">int</span>);</span><br><span class="line">	<span class="type">int</span> (*lock) (<span class="keyword">struct</span> file *, <span class="type">int</span>, <span class="keyword">struct</span> file_lock *);</span><br><span class="line">	<span class="type">ssize_t</span> (*sendpage) (<span class="keyword">struct</span> file *, <span class="keyword">struct</span> page *, <span class="type">int</span>, <span class="type">size_t</span>, <span class="type">loff_t</span> *, <span class="type">int</span>);</span><br><span class="line">	<span class="type">unsigned</span> <span class="title function_">long</span> <span class="params">(*get_unmapped_area)</span><span class="params">(<span class="keyword">struct</span> file *, <span class="type">unsigned</span> <span class="type">long</span>, <span class="type">unsigned</span> <span class="type">long</span>, <span class="type">unsigned</span> <span class="type">long</span>, <span class="type">unsigned</span> <span class="type">long</span>)</span>;</span><br><span class="line">	<span class="type">int</span> (*check_flags)(<span class="type">int</span>);</span><br><span class="line">	<span class="type">int</span> (*flock) (<span class="keyword">struct</span> file *, <span class="type">int</span>, <span class="keyword">struct</span> file_lock *);</span><br><span class="line">	<span class="type">ssize_t</span> (*splice_write)(<span class="keyword">struct</span> pipe_inode_info *, <span class="keyword">struct</span> file *, <span class="type">loff_t</span> *, <span class="type">size_t</span>, <span class="type">unsigned</span> <span class="type">int</span>);</span><br><span class="line">	<span class="type">ssize_t</span> (*splice_read)(<span class="keyword">struct</span> file *, <span class="type">loff_t</span> *, <span class="keyword">struct</span> pipe_inode_info *, <span class="type">size_t</span>, <span class="type">unsigned</span> <span class="type">int</span>);</span><br><span class="line">	<span class="type">int</span> (*setlease)(<span class="keyword">struct</span> file *, <span class="type">long</span>, <span class="keyword">struct</span> file_lock **);</span><br><span class="line">	<span class="type">long</span> (*fallocate)(<span class="keyword">struct</span> file *file, <span class="type">int</span> mode, <span class="type">loff_t</span> offset,</span><br><span class="line">				  <span class="type">loff_t</span> len);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>



<p>可见，在该成员中包含了所有文件操作相关的函数指针。</p>
<p>每一个打开的文件都对应于一个file结构体，在该结构体中，f_flags描述了文件标志，f_pos描述了文件的偏移位置，而在f_path中有含有一个指向一个inode结点的指针。</p>
<p>因此可以知道，文件表项的数据结构就是file结构体，而在实际上内核中也并不存在这样一张文件表，只是每个打开的文件都对应一个file结构体，也就是一个文件表项，**打开文件描述符表struct file fd_array[NR_OPEN_DEFAULT]**数组中的每一项都会指向这样一个文件表项，如下图所示：</p>
<img src="fd2.png" style="zoom:80%;" />



<h2 id="Inode"><a href="#Inode" class="headerlink" title="Inode"></a>Inode</h2><p>第三种数据结构就是inode节点，在<strong>include&#x2F;linux&#x2F;fs.h</strong>中找到其定义如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">inode</span> &#123;</span></span><br><span class="line">	<span class="comment">/* RCU path lookup touches following: */</span></span><br><span class="line">	<span class="type">umode_t</span>			i_mode;</span><br><span class="line">	<span class="comment">//权限</span></span><br><span class="line">	<span class="type">uid_t</span>			i_uid;</span><br><span class="line">	<span class="comment">//用户id</span></span><br><span class="line">	<span class="type">gid_t</span>			i_gid;</span><br><span class="line">	<span class="comment">//组id</span></span><br><span class="line">	<span class="type">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">inode_operations</span>	*<span class="title">i_op</span>;</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">super_block</span>	*<span class="title">i_sb</span>;</span></span><br><span class="line">	<span class="type">spinlock_t</span>		i_lock;</span><br><span class="line">	<span class="comment">/* i_blocks, i_bytes, maybe i_size */</span></span><br><span class="line">	<span class="type">unsigned</span> <span class="type">int</span>		i_flags;</span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">mutex</span>		<span class="title">i_mutex</span>;</span></span><br><span class="line">	<span class="type">unsigned</span> <span class="type">long</span>		i_state;</span><br><span class="line">	<span class="type">unsigned</span> <span class="type">long</span>		dirtied_when;</span><br><span class="line">	<span class="comment">/* jiffies of first dirtying */</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">hlist_node</span>	<span class="title">i_hash</span>;</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">list_head</span>	<span class="title">i_wb_list</span>;</span></span><br><span class="line">	<span class="comment">/* backing dev IO list */</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">list_head</span>	<span class="title">i_lru</span>;</span></span><br><span class="line">	<span class="comment">/* inode LRU list */</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">list_head</span>	<span class="title">i_sb_list</span>;</span></span><br><span class="line">	<span class="class"><span class="keyword">union</span> &#123;</span></span><br><span class="line">		<span class="class"><span class="keyword">struct</span> <span class="title">list_head</span>	<span class="title">i_dentry</span>;</span></span><br><span class="line">		<span class="class"><span class="keyword">struct</span> <span class="title">rcu_head</span>		<span class="title">i_rcu</span>;</span></span><br><span class="line">	&#125;;</span><br><span class="line">	<span class="type">unsigned</span> <span class="type">long</span>		i_ino;</span><br><span class="line">	<span class="comment">//inode节点号</span></span><br><span class="line">	<span class="type">atomic_t</span>		i_count;</span><br><span class="line">	<span class="type">unsigned</span> <span class="type">int</span>		i_nlink;</span><br><span class="line">	<span class="type">dev_t</span>			i_rdev;</span><br><span class="line">	<span class="type">unsigned</span> <span class="type">int</span>		i_blkbits;</span><br><span class="line">	u64			i_version;</span><br><span class="line">	<span class="type">loff_t</span>			i_size;</span><br><span class="line">	<span class="comment">//文件大小</span></span><br><span class="line">	<span class="meta">#<span class="keyword">ifdef</span> __NEED_I_SIZE_ORDERED</span></span><br><span class="line">		<span class="type">seqcount_t</span>		i_size_seqcount;</span><br><span class="line">	<span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">		<span class="class"><span class="keyword">struct</span> <span class="title">timespec</span>		<span class="title">i_atime</span>;</span></span><br><span class="line">	<span class="comment">//最后一次访问(access)的时间</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">timespec</span>		<span class="title">i_mtime</span>;</span></span><br><span class="line">	<span class="comment">//最后一次修改(modify)的时间</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">timespec</span>		<span class="title">i_ctime</span>;</span></span><br><span class="line">	<span class="comment">//最后一次改变(change)的时间</span></span><br><span class="line">	<span class="type">blkcnt_t</span>		i_blocks;</span><br><span class="line">	<span class="comment">//块数</span></span><br><span class="line">	<span class="type">unsigned</span> <span class="type">short</span>          i_bytes;</span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">rw_semaphore</span>	<span class="title">i_alloc_sem</span>;</span></span><br><span class="line">	<span class="type">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">file_operations</span>	*<span class="title">i_fop</span>;</span></span><br><span class="line">	<span class="comment">/* former -&gt;i_op-&gt;default_file_ops */</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">file_lock</span>	*<span class="title">i_flock</span>;</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">address_space</span>	*<span class="title">i_mapping</span>;</span></span><br><span class="line">	<span class="comment">//块地址映射</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">address_space</span>	<span class="title">i_data</span>;</span></span><br><span class="line">	<span class="meta">#<span class="keyword">ifdef</span> CONFIG_QUOTA</span></span><br><span class="line">		<span class="class"><span class="keyword">struct</span> <span class="title">dquot</span>		*<span class="title">i_dquot</span>[<span class="title">MAXQUOTAS</span>];</span></span><br><span class="line">	<span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">		<span class="class"><span class="keyword">struct</span> <span class="title">list_head</span>	<span class="title">i_devices</span>;</span></span><br><span class="line">	<span class="class"><span class="keyword">union</span> &#123;</span></span><br><span class="line">		<span class="class"><span class="keyword">struct</span> <span class="title">pipe_inode_info</span>	*<span class="title">i_pipe</span>;</span></span><br><span class="line">		<span class="class"><span class="keyword">struct</span> <span class="title">block_device</span>	*<span class="title">i_bdev</span>;</span></span><br><span class="line">		<span class="class"><span class="keyword">struct</span> <span class="title">cdev</span>		*<span class="title">i_cdev</span>;</span></span><br><span class="line">	&#125;;</span><br><span class="line">	__u32			i_generation;</span><br><span class="line">	<span class="meta">#<span class="keyword">ifdef</span> CONFIG_FSNOTIFY</span></span><br><span class="line">		__u32			i_fsnotify_mask;</span><br><span class="line">	<span class="comment">/* all events this inode cares about */</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">hlist_head</span>	<span class="title">i_fsnotify_marks</span>;</span></span><br><span class="line">	<span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">	<span class="meta">#<span class="keyword">ifdef</span> CONFIG_IMA</span></span><br><span class="line">		<span class="type">atomic_t</span>		i_readcount;</span><br><span class="line">	<span class="comment">/* struct files open RO */</span></span><br><span class="line">	<span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">		<span class="type">atomic_t</span>		i_writecount;</span><br><span class="line">	<span class="meta">#<span class="keyword">ifdef</span> CONFIG_SECURITY</span></span><br><span class="line">		<span class="type">void</span>			*i_security;</span><br><span class="line">	<span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">	<span class="meta">#<span class="keyword">ifdef</span> CONFIG_FS_POSIX_ACL</span></span><br><span class="line">		<span class="class"><span class="keyword">struct</span> <span class="title">posix_acl</span>	*<span class="title">i_acl</span>;</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">posix_acl</span>	*<span class="title">i_default_acl</span>;</span></span><br><span class="line">	<span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">		<span class="type">void</span>			*i_private;</span><br><span class="line">	<span class="comment">/* fs or device private pointer */</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p> 代码中只注释了一部分，通过inode结构，可以知道<strong>文件数据块的在磁盘上的位置以及文件大小</strong>等信息，这样才能使得<strong>进程能够通过file结构体来找到磁盘上相应文件的位置</strong>来进行文件读写。</p>
<p> 另外补充一点，关于inode结构体中的<strong>struct inode_operations *i_op</strong>成员，其数据结构定义在<strong>include&#x2F;linux&#x2F;fs.h</strong>中：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">inode_operations</span> &#123;</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">dentry</span> * (*<span class="title">lookup</span>) (<span class="keyword">struct</span> <span class="title">inode</span> *,<span class="keyword">struct</span> <span class="title">dentry</span> *, <span class="keyword">struct</span> <span class="title">nameidata</span> *);</span></span><br><span class="line">	<span class="type">void</span> * (*follow_link) (<span class="keyword">struct</span> dentry *, <span class="keyword">struct</span> nameidata *);</span><br><span class="line">	<span class="type">int</span> (*permission) (<span class="keyword">struct</span> inode *, <span class="type">int</span>, <span class="type">unsigned</span> <span class="type">int</span>);</span><br><span class="line">	<span class="type">int</span> (*check_acl)(<span class="keyword">struct</span> inode *, <span class="type">int</span>, <span class="type">unsigned</span> <span class="type">int</span>);</span><br><span class="line">	<span class="type">int</span> (*readlink) (<span class="keyword">struct</span> dentry *, <span class="type">char</span> __user *,<span class="type">int</span>);</span><br><span class="line">	<span class="type">void</span> (*put_link) (<span class="keyword">struct</span> dentry *, <span class="keyword">struct</span> nameidata *, <span class="type">void</span> *);</span><br><span class="line">	<span class="type">int</span> (*create) (<span class="keyword">struct</span> inode *,<span class="keyword">struct</span> dentry *,<span class="type">int</span>, <span class="keyword">struct</span> nameidata *);</span><br><span class="line">	<span class="type">int</span> (*link) (<span class="keyword">struct</span> dentry *,<span class="keyword">struct</span> inode *,<span class="keyword">struct</span> dentry *);</span><br><span class="line">	<span class="type">int</span> (*unlink) (<span class="keyword">struct</span> inode *,<span class="keyword">struct</span> dentry *);</span><br><span class="line">	<span class="type">int</span> (*symlink) (<span class="keyword">struct</span> inode *,<span class="keyword">struct</span> dentry *,<span class="type">const</span> <span class="type">char</span> *);</span><br><span class="line">	<span class="type">int</span> (*mkdir) (<span class="keyword">struct</span> inode *,<span class="keyword">struct</span> dentry *,<span class="type">int</span>);</span><br><span class="line">	<span class="type">int</span> (*rmdir) (<span class="keyword">struct</span> inode *,<span class="keyword">struct</span> dentry *);</span><br><span class="line">	<span class="type">int</span> (*mknod) (<span class="keyword">struct</span> inode *,<span class="keyword">struct</span> dentry *,<span class="type">int</span>,<span class="type">dev_t</span>);</span><br><span class="line">	<span class="type">int</span> (*rename) (<span class="keyword">struct</span> inode *, <span class="keyword">struct</span> dentry *,</span><br><span class="line">				<span class="keyword">struct</span> inode *, <span class="keyword">struct</span> dentry *);</span><br><span class="line">	<span class="type">void</span> (*truncate) (<span class="keyword">struct</span> inode *);</span><br><span class="line">	<span class="type">int</span> (*setattr) (<span class="keyword">struct</span> dentry *, <span class="keyword">struct</span> iattr *);</span><br><span class="line">	<span class="type">int</span> (*getattr) (<span class="keyword">struct</span> vfsmount *mnt, <span class="keyword">struct</span> dentry *, <span class="keyword">struct</span> kstat *);</span><br><span class="line">	<span class="type">int</span> (*setxattr) (<span class="keyword">struct</span> dentry *, <span class="type">const</span> <span class="type">char</span> *,<span class="type">const</span> <span class="type">void</span> *,<span class="type">size_t</span>,<span class="type">int</span>);</span><br><span class="line">	<span class="type">ssize_t</span> (*getxattr) (<span class="keyword">struct</span> dentry *, <span class="type">const</span> <span class="type">char</span> *, <span class="type">void</span> *, <span class="type">size_t</span>);</span><br><span class="line">	<span class="type">ssize_t</span> (*listxattr) (<span class="keyword">struct</span> dentry *, <span class="type">char</span> *, <span class="type">size_t</span>);</span><br><span class="line">	<span class="type">int</span> (*removexattr) (<span class="keyword">struct</span> dentry *, <span class="type">const</span> <span class="type">char</span> *);</span><br><span class="line">	<span class="type">void</span> (*truncate_range)(<span class="keyword">struct</span> inode *, <span class="type">loff_t</span>, <span class="type">loff_t</span>);</span><br><span class="line">	<span class="type">int</span> (*fiemap)(<span class="keyword">struct</span> inode *, <span class="keyword">struct</span> fiemap_extent_info *, u64 start,</span><br><span class="line">			      u64 len);</span><br><span class="line">&#125;____cacheline_aligned;</span><br></pre></td></tr></table></figure>

<p> 可见，在该成员变量所指向的数据结构中，包含了许多函数指针，这些函数指针大多针对于目录、文件操作。</p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p> 进程、打开文件描述符表、文件表项和i-node结点关系如图所示。（暂时忽略进程中0、1和2号文件描述符分别默认为标准输入、标准输出和标准错误的情况）</p>
<p><img src="/2021/11/20/Linux%E6%96%87%E4%BB%B6%E6%8F%8F%E8%BF%B0%E7%AC%A6%E7%9A%84%E5%AE%9E%E7%8E%B0/fd1.png"></p>
<p>通过以上分析，我们可以得出以下结论：</p>
<ol>
<li><p>每启动一个进程都会为其分配一个<code>task_struct</code>结构体，在task_struct结构体中含有一个<code>file_struct</code>结构体指针，其所指向的file_struct结构体中，含有一个file的指针数组<code>fd_array</code>,它就是<strong>打开文件描述符表</strong>，其中<strong>每一个元素都指向一个文件表项</strong>，这个<strong>数组的索引就是文件描述符</strong>。此外，file_struct结构体中的next_fd保存的是下一个分配的文件描述符，它会在调用open和close改变，最终使得每次open返回的都是当前可用的<strong>最小文件描述符</strong>；</p>
</li>
<li><p>每次调用open或者create(内部实际上还是调用的open)，都会对新打开的文件分配一个file结构体，并且将打开文件的标志、状态、权限等信息填入这个file结构体中。<strong>这个file结构体也叫文件表项</strong>；</p>
</li>
<li><p><strong>磁盘中的每个文件都对应一个i-node</strong>，<strong>每一个文件表项都会指向一个文件的i-node</strong>，<strong>但是同一文件的i-node可以对应多个文件表项（</strong>当多次调用open打开同一个文件时就会出现这种情况，不管是同一进程多次打开同一文件（如图中A进程的0号和2号文件描述符对应两个文件表项，但是最终指向同一i-node即同一文件），还是不同进程多次打开同一文件（如图中A进程3号文件描述符和B进程的3号文件描述符））；</p>
</li>
<li><p><strong>同一进程下的不同文件描述符是可以指向同一文件表项</strong>，即最终指向同一文件<strong>（如图中A进程的0号文件描述符和1号文件描述符，使用dup函数即可实现）。</strong></p>
</li>
<li><p>子进程在创建时会拷贝父进程的打开文件描述符表，因此<strong>父子进程是共享文件表项</strong>的。而相互独立的不同进程的打开文件描述符表是相互独立的，因此相互独立的多个进程之间的文件描述符可以相同，但是不同进程的文件描述符是不能指向同一文件表项的（<strong>除非这个文件描述符是从同一个祖先进程中继承得来的</strong>），但是这并不妨碍不同进程访问同一文件（如第3点结论）；</p>
</li>
<li><p>指向同一文件表项的不同文件描述符（不同进程相同数值的文件描述符也看做不同）共享文件标志、文件偏移等信息；</p>
</li>
<li><p>每一个文件表项对应的file结构体中的<code>f_count</code>会记录通过该文件表项打开文件的次数，当<code>f_count</code>计数归0时这个文件表项才会被删除，因此，对于指向同一文件表项的两个不同文件描述符（如子进程所继承的父进程的文件描述符，或同一进程中dup的两个文件描述符指向同一个文件表项），即使其中一个文件描述符关闭了，只要仍然有文件描述符指向这个文件表项，那么就依然能通过这个文件表项访问文件，直到所有指向该文件表项的文件描述符都关闭了才不能再进行访问；</p>
</li>
</ol>
<blockquote>
<p>参考链接</p>
<p><a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/%E6%96%87%E4%BB%B6%E6%8F%8F%E8%BF%B0%E7%AC%A6">文件描述符</a></p>
<p><a target="_blank" rel="noopener" href="https://www.cnblogs.com/lwyeric/p/13598704.html">Linux中文件描述符的理解</a></p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




  <nav class="pagination">
    <a class="extend prev" rel="prev" title="上一页" aria-label="上一页" href="/page/17/"><i class="fa fa-angle-left"></i></a><a class="page-number" href="/">1</a><span class="space">&hellip;</span><a class="page-number" href="/page/17/">17</a><span class="page-number current">18</span><a class="page-number" href="/page/19/">19</a><span class="space">&hellip;</span><a class="page-number" href="/page/26/">26</a><a class="extend next" rel="next" title="下一页" aria-label="下一页" href="/page/19/"><i class="fa fa-angle-right"></i></a>
  </nav>

</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 
  <span itemprop="copyrightYear">2023</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">SongyangJi</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/muse/" rel="noopener" target="_blank">NexT.Muse</a> 强力驱动
  </div>

    </div>
  </footer>

  
  <div class="toggle sidebar-toggle" role="button">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>
  <div class="sidebar-dimmer"></div>
  <div class="back-to-top" role="button" aria-label="返回顶部">
    <i class="fa fa-arrow-up fa-lg"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/schemes/muse.js"></script><script src="/js/next-boot.js"></script>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/hexo-generator-searchdb/1.4.1/search.js" integrity="sha256-1kfA5uHPf65M5cphT2dvymhkuyHPQp5A53EGZOnOLmc=" crossorigin="anonymous"></script>
<script src="/js/third-party/search/local-search.js"></script>





  





</body>
</html>
